Analyze Data
Decalarative SQL to interact with many data
Leverage query engine for data processing
Readable, maintainable, tranferable
Department of Energy’s EIA: Monthly Energy Review &
NOAA Mauna Loa, Hawaii Observatory - Carbon PPM
SELECT p.date_year,
sum(p.average_ppm) as carbon_ppm_total,
avg(p.average_ppm) as carbon_ppm_mean,
sum(c.energy_consumed) as btu_consumed_total,
avg(c.energy_consumed) as btu_consumed_mean,
sum(e.energy_co2) as co2_emissions_total,
avg(e.energy_co2) as co2_emissions_mean
FROM ppm_month p
JOIN consumption c
ON p.date_year = c.date_year AND p.date_month = c.date_month and c.msn = 'TXRCBUS'
JOIN us_co2_emissions e
ON p.date_year = e.date_year AND p.date_month = e.date_month and e.msn = 'TETCEUS'
GROUP BY p.date_year
ORDER BY p.date_year
kable_styling(kable(tail(agg_df, 10)),
bootstrap_options = c("striped", "hover"))
| |
date_year |
carbon_ppm_total |
carbon_ppm_mean |
btu_consumed_total |
btu_consumed_mean |
co2_emissions_total |
co2_emissions_mean |
| 38 |
2010 |
4678.79 |
389.8992 |
6641.355 |
553.4462 |
5585.741 |
465.4784 |
| 39 |
2011 |
4699.83 |
391.6525 |
6473.666 |
539.4722 |
5446.133 |
453.8444 |
| 40 |
2012 |
4726.24 |
393.8533 |
5684.503 |
473.7086 |
5237.300 |
436.4417 |
| 41 |
2013 |
4758.25 |
396.5208 |
6689.368 |
557.4473 |
5363.018 |
446.9182 |
| 42 |
2014 |
4783.77 |
398.6475 |
7007.139 |
583.9283 |
5411.193 |
450.9327 |
| 43 |
2015 |
4810.01 |
400.8342 |
6465.092 |
538.7577 |
5264.776 |
438.7313 |
| 44 |
2016 |
4850.87 |
404.2392 |
6033.098 |
502.7582 |
5172.402 |
431.0335 |
| 45 |
2017 |
4878.64 |
406.5533 |
6111.580 |
509.2983 |
5130.589 |
427.5491 |
| 46 |
2018 |
4902.26 |
408.5217 |
6896.516 |
574.7097 |
5267.750 |
438.9792 |
| 47 |
2019 |
410.83 |
410.8300 |
1163.200 |
1163.2000 |
493.839 |
493.8390 |
SELECT p.date_year, p.date_month, CONCAT(p.date_year, '-', p.date_month, '-01')::date as date_day,
p.average_ppm as "carbon ppm", c.energy_consumed as "btu consumed", e.energy_co2 as "co2 emissions"
FROM ppm_month p
JOIN consumption c
ON p.date_year = c.date_year AND p.date_month = c.date_month and c.msn = 'TXRCBUS'
JOIN us_co2_emissions e
ON p.date_year = e.date_year AND p.date_month = e.date_month and e.msn = 'TETCEUS'
ORDER BY p.date_year, p.date_month
metric_ts <- xts(x=metrics_df[c("carbon ppm", "btu consumed", "co2 emissions")],
order.by=metrics_df$date_day)
plot(metric_ts, main = "Energy and Carbon PPM Metrics",
legend.loc="bottomright",
col = seaborn_palette[1:3],
yaxis.right=FALSE,
axes=FALSE,
lwd=1,
cex.main=3,
major.ticks="years",
major.format="%Y",
minor.format="%Y",
grid.ticks.lty=1)

Seasonal Decomposition
carbonppm_ts <- ts(metrics_df$`carbon ppm`, start=c(1973, 1), frequency=12)
carbonppm_stl <- stl(carbonppm_ts, s.window="periodic")
plot(carbonppm_stl, col = seaborn_palette[1],
main="Seasonal Decomposition of Global Carbon PPM")

consumed_ts <- ts(metrics_df$`btu consumed`, start=c(1973, 1), frequency=12)
consumed_stl <- stl(consumed_ts, s.window="periodic")
plot(consumed_stl, col = seaborn_palette[2],
main="Seasonal Decomposition of U.S. Energy Consumption")

emissions_ts <- ts(metrics_df$`co2 emissions`, start=c(1973, 1), frequency=12)
emissions_stl <- stl(emissions_ts, s.window="periodic")
plot(emissions_stl, col = seaborn_palette[3],
main = "Seasonal Decomposition of U.S. CO2 Emissions")

sql <- "WITH c1 AS
(SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE(description, 'Primary Energy Consumed by the ', '') AS \"Sector\",
SUM(energy_consumed) AS \"Primary Energy Consumed\"
FROM consumption
WHERE msn IN ('TXICBUS', 'TXRCBUS', 'TXACBUS', 'TXCCBUS', 'TXEIBUS')
GROUP BY CONCAT((date_year/10)::int * 10, 's'), description
), c2 AS
(SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE(description, 'Total Energy Consumed by the ', '') AS \"Sector\",
SUM(energy_consumed) AS \"Total Energy Consumed\"
FROM consumption
WHERE msn IN ('TECCBUS', 'TEACBUS', 'TEICBUS', 'TERCBUS')
GROUP BY CONCAT((date_year/10)::int * 10, 's'), description
)
SELECT c1.decade, c1.\"Sector\", c1.\"Primary Energy Consumed\", c2.\"Total Energy Consumed\"
FROM c1
LEFT JOIN c2
ON c1.\"Sector\" = c2.\"Sector\" AND c1.decade = c2.decade
ORDER BY c1.decade, c1.\"Sector\"
"
consumed_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(consumed_df)),
bootstrap_options = c("striped", "hover"))
| |
decade |
Sector |
Primary Energy Consumed |
Total Energy Consumed |
| 35 |
2000s |
Transportation Sector |
546616.33 |
548112.4 |
| 36 |
2010s |
Commercial Sector |
77171.71 |
326503.7 |
| 37 |
2010s |
Electric Power Sector |
693591.66 |
NA |
| 38 |
2010s |
Industrial Sector |
388661.21 |
570178.3 |
| 39 |
2010s |
Residential Sector |
117157.95 |
378492.5 |
| 40 |
2010s |
Transportation Sector |
490149.36 |
491557.4 |
Consumption and CO2 Emissions
plot_mat <- with(subset(consumed_df, Sector != 'Electric Power Sector'),
tapply(`Total Energy Consumed`, list(decade, `Sector`), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Total Energy Consumption by Sector", cex.main=1.5,
col=seaborn_palette[1:8], ylim=c(0, 8E5), xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(5, 14, 23, 32), labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:8], ncol=8)

plot_mat <- with(consumed_df, tapply(`Primary Energy Consumed`, list(decade, `Sector`), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Primary Energy Consumption by Sector", cex.main=1.5,
col=seaborn_palette[1:8], ylim=c(0, 9E5), xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(5, 14, 23, 32, 41), labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:8], ncol=8)

sql <- "SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE(REPLACE(description, 'Total Energy ', ''), ' CO2 Emissions', '') AS \"Sector\",
SUM(energy_co2) AS \"Total CO2 Emissions\"
FROM us_co2_emissions
WHERE msn IN ('TERCEUS', 'TECCEUS', 'TEACEUS', 'TXEIEUS')
GROUP BY date_year, description
ORDER BY date_year, description
"
emissions_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(emissions_df)),
bootstrap_options = c("striped", "hover"))
| |
decade |
Sector |
Total CO2 Emissions |
| 183 |
2010s |
Residential Sector |
2034.691 |
| 184 |
2010s |
Transportation Sector |
3830.401 |
| 185 |
2010s |
Commercial Sector |
164.804 |
| 186 |
2010s |
Electric Power Sector |
274.988 |
| 187 |
2010s |
Residential Sector |
223.165 |
| 188 |
2010s |
Transportation Sector |
295.360 |
plot_mat <- with(emissions_df, tapply(`Total CO2 Emissions`, list(decade, `Sector`), sum))
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. CO2 Emissions by Sector", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 6E4), xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(3, 9, 15, 21)+0.5, labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "WITH sub AS
(SELECT CONCAT((date_year/10)::int * 10, 's') as decade,
REPLACE($1, '%', '') as sector,
REPLACE(
REPLACE(
REPLACE(
REPLACE(description, 'Commercial Sector CO2 Emissions', ''),
'Residential Sector CO2 Emissions', ''
), 'Transportation Sector CO2 Emissions', ''),
' ', '\n') AS \"Type\",
energy_co2
FROM us_co2_emissions
WHERE description LIKE $2)
SELECT decade, sector, \"Type\",
SUM(energy_co2) AS \"Total CO2 Emissions\"
FROM sub
GROUP BY decade, sector, \"Type\"
ORDER BY decade, sector, \"Type\"
"
params <- paste0(c("%Transportation", "%Residential", "%Commercial"), " Sector%")
emissions_type_df <- do.call(rbind, lapply(params, function(p) dbGetQuery(conn, sql, param=list(p, p))))
output <- by(emissions_type_df, emissions_type_df$sector, function(sub) {
plot_mat <- with(sub, tapply(`Total CO2 Emissions`, list(decade, `Type`), sum))
barplot(plot_mat, main=paste("U.S. CO2 Emissions by", sub$sector[[1]]), cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, ceiling(max(plot_mat, na.rm=TRUE)/1E4) * 1E4),
xaxt="n", yaxt="n", beside=TRUE)
axis(side=1, at=c(3,9,15,21,27,33,39,45,51,57,63)[seq_along(colnames(plot_mat))] + 0.5,
labels=colnames(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)
})



Renewable Energy: Production and Consumption
sql <- "SELECT energy_type,
date,
SUM(production) AS production,
SUM(consumption) AS consumption
FROM us_renewable_energy
GROUP BY energy_type,
date
ORDER BY energy_type,
date
"
renewable_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(renewable_df)),
bootstrap_options = c("striped", "hover"))
| |
energy_type |
date |
production |
consumption |
| 4678 |
Wood Energy |
2019-02-01 |
190.887 |
182.491 |
| 4679 |
Wood Energy |
2019-03-01 |
198.621 |
191.507 |
| 4680 |
Wood Energy |
2019-04-01 |
195.791 |
187.670 |
| 4681 |
Wood Energy |
2019-05-01 |
201.743 |
193.775 |
| 4682 |
Wood Energy |
2019-06-01 |
198.379 |
189.036 |
| 4683 |
Wood Energy |
2019-07-01 |
205.023 |
196.873 |
par(mfrow=c(3,3), mar=c(5, 5, 2, 1))
output <- by(renewable_df, renewable_df$energy_type, function(sub) {
metric_ts <- xts(x=sub[c("production", "consumption")],
order.by=sub$date)
print(plot(metric_ts, main = sub$energy_type[1],
legend.loc="bottomright",
col = seaborn_palette[1:3],
yaxis.right=FALSE,
axes=FALSE,
lwd=1,
cex.main=3,
major.ticks="years",
major.format="%Y",
minor.format="%Y",
grid.ticks.lty=1))
})

U.S. Geological Survey - Groundwater Well Depth Data
sql <- "SELECT g.year, g.month,
avg(g.mean_value) as mean_value
FROM groundwater g
WHERE g.year BETWEEN 1990 AND 2019
GROUP BY g.year, g.month"
groundwater_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(groundwater_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
month |
mean_value |
| 350 |
2019 |
2 |
63735.69 |
| 351 |
2019 |
3 |
60792.13 |
| 352 |
2019 |
4 |
54698.44 |
| 353 |
2019 |
5 |
53237.05 |
| 354 |
2019 |
6 |
49160.99 |
| 355 |
2019 |
7 |
76941.00 |
boxplot(mean_value ~ year, groundwater_df, col=seaborn_palette[1:10],
main="Groundwater Well Depth Mean Values", cex.main=1.5)

sql <- "WITH sites AS (
SELECT CONCAT(g.year, '-', g.month, '-1')::date AS \"date\",
g.site_name,
AVG(g.mean_value) as mean_value
FROM groundwater g
WHERE g.year BETWEEN 1990 AND 2019
GROUP BY CONCAT(g.year, '-', g.month, '-1')::date,
g.site_name
), bottom AS (
SELECT site_name, AVG(mean_value) AS mean_value, 'bottom_sites' AS category
FROM sites
GROUP BY site_name
ORDER BY AVG(mean_value) ASC LIMIT 5
), top AS (
SELECT site_name, AVG(mean_value) AS mean_value, 'top_sites' AS category
FROM sites
GROUP BY site_name
ORDER BY AVG(mean_value) DESC LIMIT 5
)
SELECT sites.\"date\", sites.mean_value, sites.site_name, top.category
FROM sites
INNER JOIN top
ON sites.site_name = top.site_name
UNION
SELECT sites.\"date\", sites.mean_value, sites.site_name, bottom.category
FROM sites
INNER JOIN bottom
ON sites.site_name = bottom.site_name
ORDER BY category, site_name, \"date\""
groundwater_df <- dbGetQuery(conn, sql)
kable_styling(kable(head(groundwater_df)),
bootstrap_options = c("striped", "hover"))
| date |
mean_value |
site_name |
category |
| 1991-06-01 |
31 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-07-01 |
35 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-08-01 |
41 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-09-01 |
42 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-10-01 |
36 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
| 1991-11-01 |
40 |
FAIRPLAINS 2 WELL~ ST. CROIX~ USVI |
bottom_sites |
groundwater_df$year <- format(groundwater_df$date, "%Y")
par(mfrow=c(5,2), mar=c(5, 5, 2, 1))
output <- by(groundwater_df, groundwater_df$site_name, function(sub) {
metric_ts <- xts(x=sub[c("mean_value")],
order.by=sub$date)
print(plot(metric_ts, main = paste(sub$site_name[1], ':', sub$category[1]),
legend.loc="bottomright",
col = seaborn_palette[1:3],
yaxis.right=FALSE,
axes=FALSE,
lwd=1,
cex.main=3,
major.ticks="years",
major.format="%Y",
minor.format="%Y",
grid.ticks.lty=1))
})

U.S. FWS Endangered Species List
sql <- "SELECT CONCAT((f.date_year/10)::int * 10, 's') AS decade,
f.taxonomic_group,
f.current_status,
count(*) AS species_count
FROM fws_species_year f
WHERE f.current_status IN ('Endangered', 'Recovery', 'Resolved Taxon',
'Threatened', 'Extinction')
GROUP BY CONCAT((f.date_year/10)::int * 10, 's'),
f.taxonomic_group,
f.current_status
ORDER BY CONCAT((f.date_year/10)::int * 10, 's'),
f.taxonomic_group,
f.current_status"
fws_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(fws_df)),
bootstrap_options = c("striped", "hover"))
| |
decade |
taxonomic_group |
current_status |
species_count |
| 141 |
2010s |
Mammals |
Resolved Taxon |
1 |
| 142 |
2010s |
Mammals |
Threatened |
11 |
| 143 |
2010s |
Reptiles |
Endangered |
4 |
| 144 |
2010s |
Reptiles |
Threatened |
9 |
| 145 |
2010s |
Snails |
Endangered |
18 |
| 146 |
2010s |
Snails |
Threatened |
1 |
par(mfrow=c(5,3), mar=c(5, 5, 2, 1))
output <- by(fws_df, fws_df$taxonomic_group, function(sub) {
plot_mat <- with(sub, tapply(species_count, list(decade, current_status), sum))
barplot(plot_mat, main=sub$taxonomic_group[[1]], cex.main=1.5,
ylim = c(0, max(plot_mat, na.rm=TRUE)+5),
col=seaborn_palette[seq_along(row.names(plot_mat))], beside=TRUE)
legend("top", row.names(plot_mat), fill=seaborn_palette[seq_along(row.names(plot_mat))],
ncol=length(row.names(plot_mat)))
box()
})

U.S. Department of Agriculture: Agriculture Census
sql <- "SELECT year,
CASE
WHEN domain_category = '' THEN 'TOTAL\nOPERATIONS'
ELSE REPLACE(REPLACE(REPLACE(domain_category, '(', '\n('), 'TO', 'TO\n'), 'OR', 'OR\n')
END AS domain_category,
value
FROM ag_census
WHERE data_item = 'FARM OPERATIONS - NUMBER OF OPERATIONS'"
agcensus_df <- dbGetQuery(conn, sql)
plot_mat <- with(agcensus_df, tapply(value, list(year, domain_category), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Number of Farm Operations", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 2E6+5E5), beside=TRUE, xaxt="n", yaxt="n")
axis(side=1, at=c(3,9,15,21,27,33,39,45)+0.5, labels=colnames(plot_mat), tick=FALSE, padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

sql <- "WITH sub AS (
SELECT year,
data_item,
REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE(domain_category, ';', ','),
'(', '\n('),
'TO', 'TO\n'),
' OR', ' OR\n'),
'LESS', 'LESS\n') AS domain_category,
value
FROM ag_census
WHERE data_item LIKE '%COMMODITY TOTALS%'
AND value IS NOT NULL
)
SELECT year,
CASE
WHEN TRIM(domain_category) = ''
THEN CASE
WHEN data_item = 'COMMODITY TOTALS - SALES; MEASURED IN $'
THEN 'TOTAL\nSALES'
WHEN data_item = 'COMMODITY TOTALS - SALES; MEASURED IN $ / OPERATION'
THEN 'TOTAL\nSALES\nPER OPERATION'
END
ELSE domain_category
END AS domain_category,
value
FROM sub
"
agcensus_df <- within(dbGetQuery(conn, sql),
domain_category <- factor(domain_category,
levels = c("FARM SALES: \n(LESS\n THAN 2,500 $)", "FARM SALES: \n(2,500 TO\n 4,999 $)",
"FARM SALES: \n(5,000 TO\n 9,999 $)", "FARM SALES: \n(10,000 TO\n 24,999 $)",
"FARM SALES: \n(25,000 TO\n 49,999 $)", "FARM SALES: \n(50,000 TO\n 99,999 $)",
"FARM SALES: \n(100,000 TO\n 499,999 $)", "FARM SALES: \n(500,000 OR\n MORE $)",
"TOTAL\nSALES", "TOTAL\nSALES\nPER OPERATION"))
)
kable_styling(kable(tail(agcensus_df)),
bootstrap_options = c("striped", "hover"))
| |
year |
domain_category |
value |
| 45 |
2017 |
FARM SALES:
(5,000 TO
9,999 $) |
208074 |
| 46 |
2017 |
FARM SALES:
(10,000 TO
24,999 $) |
228218 |
| 47 |
2017 |
FARM SALES:
(25,000 TO
49,999 $) |
144113 |
| 48 |
2017 |
FARM SALES:
(50,000 TO
99,999 $) |
119434 |
| 49 |
2017 |
FARM SALES:
(100,000 TO
499,999 $) |
218771 |
| 50 |
2017 |
FARM SALES:
(500,000 OR
MORE $) |
146568 |
plot_mat <- with(agcensus_df[agcensus_df$domain_category != 'TOTAL\nSALES',],
tapply(value, list(year, factor(domain_category)), sum))
par(mar=c(5, 5, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Commodity Totals - Farm Sales", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 1E6), beside=TRUE, xaxt="n", yaxt="n")
axis(side=1, at=c(3,9,15,21,27,33,39,45,51)+0.5, labels=colnames(plot_mat), tick=FALSE, padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

plot_mat <- with(agcensus_df[agcensus_df$domain_category == 'TOTAL\nSALES',],
tapply(value, list(year, factor(domain_category)), sum))
par(mar=c(5, 8, 2, 1))
barplot(plot_mat, main="U.S. Agriculture Census: Commodity Totals - Overall Farm Sales", cex.main=1.5,
col=seaborn_palette[1:5], ylim=c(0, 4E11+5E10), beside=TRUE, xaxt="n", yaxt="n", space=2)
axis(side=1, at=c(2.5,5.5,8.5,11.5,14.5), labels=row.names(plot_mat), padj=0.9, pos=c(0,0))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE), las=1)
legend("top", row.names(plot_mat), fill=seaborn_palette[1:5], ncol=5)

World Metrics
sql <- "WITH pop AS
(SELECT p.year,
p.population
FROM world_population p
WHERE p.country_name = 'World'
AND p.year BETWEEN 2000 AND 2019
),
land AS
(SELECT a.year,
a.percent_arable
FROM arable_land a
WHERE a.country_name = 'World'
AND a.year BETWEEN 2000 AND 2019
),
fauna AS
(SELECT i.year,
SUM(i.species_count) AS animals_count
FROM iucn_species_count i
WHERE i.year BETWEEN 2000 AND 2019
GROUP BY i.year
),
flora AS
(SELECT p.assessment_year as year,
COUNT(*) AS plants_count
FROM plants_assessments p
WHERE p.assessment_year BETWEEN 2000 AND 2019
AND p.interpreted_status = 'Threatened'
GROUP BY p.assessment_year
),
ice AS
(SELECT s.date_year as year,
AVG(s.extent) FILTER(WHERE s.region = 'Arctic') AS arctic_sea_ice_extent,
AVG(s.extent) FILTER(WHERE s.region = 'Antarctica') AS antarctic_sea_ice_extent
FROM sea_ice_extent s
WHERE s.date_year BETWEEN 2000 AND 2019
GROUP BY s.date_year
),
ocean AS
(SELECT o.year as year,
AVG(o.tco2) AS total_co2,
AVG(o.phts25p0) AS ph_scale
FROM ocean_data o
WHERE o.year BETWEEN 2000 AND 2019
AND o.tco2 <> -9999 AND o.phts25p0 <> -9999
GROUP BY o.year
),
temp AS
(SELECT g.year as year,
AVG(g.global_mean) AS global_mean
FROM global_temperature g
WHERE g.year BETWEEN 2000 AND 2019
GROUP BY g.year
)
SELECT pop.year, pop.population, land.percent_arable, fauna.animals_count, flora.plants_count,
ice.arctic_sea_ice_extent, ice.antarctic_sea_ice_extent,
ocean.total_co2, ocean.ph_scale, temp.global_mean
FROM pop
JOIN land USING (year)
JOIN fauna USING (year)
JOIN flora USING (year)
JOIN ice USING (year)
JOIN ocean USING (year)
JOIN temp USING (year)
ORDER BY pop.year"
env_world_df <- dbGetQuery(conn, sql)
kable_styling(kable(tail(env_world_df)), font_size = 12,
bootstrap_options = c("striped", "hover"))
| |
year |
population |
percent_arable |
animals_count |
plants_count |
arctic_sea_ice_extent |
antarctic_sea_ice_extent |
total_co2 |
ph_scale |
global_mean |
| 7 |
2008 |
6765986891 |
10.80640 |
16928 |
3279 |
10.97785 |
12.23941 |
2181.326 |
7.685694 |
0.5155556 |
| 8 |
2009 |
6849272706 |
10.77478 |
17291 |
2294 |
10.93197 |
12.04859 |
2176.104 |
7.735574 |
0.6294444 |
| 9 |
2010 |
6932596129 |
10.74432 |
18351 |
3217 |
10.71139 |
12.10679 |
2191.072 |
7.702093 |
0.7022222 |
| 10 |
2011 |
7014792135 |
10.80189 |
19570 |
2850 |
10.48350 |
11.50057 |
2224.690 |
7.675097 |
0.5844444 |
| 11 |
2012 |
7099311892 |
10.87358 |
20219 |
7556 |
10.40610 |
12.00444 |
2187.112 |
7.731700 |
0.6183333 |
| 12 |
2013 |
7184861447 |
10.89469 |
21353 |
5465 |
10.89712 |
12.52361 |
2183.400 |
7.723483 |
0.6438889 |
kable_styling(kable(cor(env_world_df[-1])), font_size = 12,
bootstrap_options = c("striped", "hover"))
| |
population |
percent_arable |
animals_count |
plants_count |
arctic_sea_ice_extent |
antarctic_sea_ice_extent |
total_co2 |
ph_scale |
global_mean |
| population |
1.0000000 |
0.1656147 |
0.9774118 |
0.7453433 |
-0.8026257 |
0.5047082 |
0.5832658 |
-0.1705909 |
0.5630321 |
| percent_arable |
0.1656147 |
1.0000000 |
0.2441270 |
0.4917138 |
0.0689147 |
0.3901610 |
-0.0431136 |
0.1567368 |
-0.3399306 |
| animals_count |
0.9774118 |
0.2441270 |
1.0000000 |
0.7450838 |
-0.8024194 |
0.5181381 |
0.6740957 |
-0.2912376 |
0.4783654 |
| plants_count |
0.7453433 |
0.4917138 |
0.7450838 |
1.0000000 |
-0.7224389 |
0.3576609 |
0.3673134 |
-0.1134181 |
0.3630865 |
| arctic_sea_ice_extent |
-0.8026257 |
0.0689147 |
-0.8024194 |
-0.7224389 |
1.0000000 |
-0.0588615 |
-0.7926131 |
0.5220906 |
-0.5461557 |
| antarctic_sea_ice_extent |
0.5047082 |
0.3901610 |
0.5181381 |
0.3576609 |
-0.0588615 |
1.0000000 |
0.1042700 |
0.0767117 |
0.0713994 |
| total_co2 |
0.5832658 |
-0.0431136 |
0.6740957 |
0.3673134 |
-0.7926131 |
0.1042700 |
1.0000000 |
-0.8165837 |
0.2653096 |
| ph_scale |
-0.1705909 |
0.1567368 |
-0.2912376 |
-0.1134181 |
0.5220906 |
0.0767117 |
-0.8165837 |
1.0000000 |
0.0463203 |
| global_mean |
0.5630321 |
-0.3399306 |
0.4783654 |
0.3630865 |
-0.5461557 |
0.0713994 |
0.2653096 |
0.0463203 |
1.0000000 |
par(mfrow=c(4,2), mai = c(0.3, 0.2, 0.7, 0.2))
for(x in colnames(env_world_df)[3:ncol(env_world_df)]) {
lfit <- loess(paste(x, "~ population"), data=env_world_df)
plot(as.formula(paste(x, "~ population")), env_world_df,
main=paste("population and\n", gsub("_", " ", x)), cex.main=2,
type="p", col=seaborn_palette[1], yaxt='n', xaxt='n', pch=19)
axis(side=1, at=axTicks(1), labels=format(axTicks(1), big.mark=',', scientific=FALSE))
axis(side=2, at=axTicks(2), labels=format(axTicks(2), big.mark=',', scientific=FALSE))
pop_order <- order(env_world_df$population)
lines(env_world_df$population[pop_order], lfit$fitted[pop_order], col=seaborn_palette[4], lwd=3)
}

dbDisconnect(conn)
[1] TRUE
User Data Application
# LINUX SHELL COMMAND CALL
system(paste0("gnome-terminal -- Rscript -e \"library(shiny); setwd('", getwd(), "'); runApp('EnvironmentDB_Shiny_App.R')\""))
Conclusion
Postgres as tool in data science workflow
Clear and expressive SQL processing
Leverage powerful database engine
Enhanced and robust process over flat files
Streamlined open source ecosystem
LS0tCnRpdGxlOiAiUG9zdGdyZVNRTCBBcyBBIERhdGEgU2NpZW5jZSBEYXRhYmFzZSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoubWFpbi1jb250YWluZXIgewogIG1heC13aWR0aDogMTAwMHB4OwogIG1hcmdpbi1sZWZ0OiBhdXRvOwogIG1hcmdpbi1yaWdodDogYXV0bzsKfQo8L3N0eWxlPgoKPGJyLz4KPGRpdiBzdHlsZT0iZm9udC1zaXplOiAyMHB4OyI+Q2hpY2FnbyBQb3N0Z3JlcyBVc2VyIEdyb3VwIE1lZXR1cDwvZGl2Pgo8ZGl2IHN0eWxlPSJmb250LXNpemU6IDE4cHg7Ij5KYW51YXJ5IDE0LCAyMDIwPC9kaXY+CjxkaXYgc3R5bGU9ImZsb2F0OmxlZnQiPjxpbWcgc3JjPSJJTUFHRVMvcG9zdGdyZXNxbF9yLnBuZyIgd2lkdGg9IjIwMHB4Ii8+PC9kaXY+Cjxici8+PGJyLz48YnIvPjxici8+CgojIyBQYXJmYWl0IEdhc2FuYSAjIwo8ZGl2IHN0eWxlPSJmb250LXNpemU6IDIwcHg7Ij5EYXRhIEFuYWx5c3QsIFdpbnN0b24gJiBTdHJhd248L2Rpdj4KPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdCI+PGltZyBzcmM9IklNQUdFUy9zdGFja292ZXJmbG93LnBuZyIgd2lkdGg9IjMwcHgiLz48L2Rpdj4KPGRpdiBzdHlsZT0iZm9udC1zaXplOiAxNnB4OyI+QFBhcmZhaXQgKFN0YWNrT3ZlcmZsb3cpIHwgPGltZyBzcmM9IklNQUdFUy9naXRodWIucG5nIiB3aWR0aD0iMzBweCIvPiZuYnNwOyZuYnNwO0BQYXJmYWl0RyAoR2l0SHViKTwvZGl2PgoKPGhyIHN0eWxlPSJib3JkZXI6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjQ0NDOyIvPgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChyb290LmRpciA9ICIvaG9tZS9wYXJmYWl0Zy9Eb2N1bWVudHMvUEdTUUwiKQpgYGAKCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48aW1nIHNyYz0iSU1BR0VTL29wZW5fc291cmNlLnBuZyIgd2lkdGg9Ijc1cHgiLz48L2Rpdj4KPGJyLz4KCiMjIE9wZW4gU291cmNlIFN1Y2Nlc3MgU3RvcmllcwoKPGJyLz4KCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48aW1nIHNyYz0iSU1BR0VTL3Bvc3RncmVzcWwuanBlZyIgd2lkdGg9IjUwcHgiLz48L2Rpdj4KLSAjIyMgUG9zdGdyZVNRTDogcG93ZXJmdWwsIGV4dGVuc2libGUsIGFuYWx5dGljYWwgUkRCTVMKICAgIC0gIyMjIyBbQ29tbXVuaXR5XShodHRwczovL3d3dy5wb3N0Z3Jlc3FsLm9yZy9jb21tdW5pdHkvKTogY29udHJpYnV0b3JzLCBtYWlsaW5nIGxpc3RzLCBsb2NhbCB1c2VyIGdyb3Vwcwo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9yX2xvZ28ucG5nIiB3aWR0aD0iNTBweCIvPjwvZGl2PgotICMjIyBSOiBkYXRhIHNjaWVuY2Ugc3RhY2ssIGV4dGVuc2libGUgZW52aXJvbm1lbnQKICAgIC0gIyMjIyBbQ1JBTl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvKSByZXBvc2l0b3J5IG9mIHBhY2thZ2VzCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48aW1nIHNyYz0iSU1BR0VTL3B5dGhvbi5wbmciIHdpZHRoPSI1MHB4Ii8+PC9kaXY+Ci0gIyMjIFB5dGhvbjogZ2VuZXJhbCBwdXJwb3NlLCBleHRlbnNpYmxlIGxhbmd1YWdlCiAgICAtICMjIyMgW1B5UEldKGh0dHBzOi8vcHlwaS5vcmcvKSBlY29zeXN0ZW0gb2YgbW9kdWxlcwoKPGRpdiBzdHlsZT0ibWFyZ2luLWxlZnQ6NDBweDsiLz48aDM+Q2hhbGxlbmdpbmcgQ29tbWVyY2lhbCBNYXJrZXQgU2hhcmU8L2gzPjwvZGl2Pgo8ZGl2IHN0eWxlPSJmbG9hdDpsZWZ0OyBtYXJnaW4tbGVmdDoxMDBweDsiLz48aW1nIHNyYz0iSU1BR0VTL2NvbW1lcmNpYWxfdG9vbHMucG5nIiB3aWR0aD0iMjAwcHgiLz48L2Rpdj4KPGJyLz4KPGJyLz4KPGJyLz4KPGJyLz4KCgo8aHIgc3R5bGU9ImJvcmRlcjogbm9uZTsgaGVpZ2h0OiAxcHg7IGJhY2tncm91bmQtY29sb3I6ICNDQ0M7Ii8+Cgo8aDI+RGF0YSBTY2llbmNlIENoYWxsZW5nZXM8L2gyPgoKLSA8aDM+RmxhdCBGaWxlcyBNYW5hZ2VtZW50OiB0ZXh0LCBiaW5hcnksIHdlYiwgZmllbGQ8L2gzPgotIDxoMz5CaWcgRGF0YTogdm9sdW1lLCB2YXJpZXR5LCB2ZWxvY2l0eSwgdmVyYWNpdHk8L2gzPgotIDxoMz5SZXByb2R1Y2liaWxpdHk6IGRhdGEsIGVudmlyb25tZW50LCB0b29sczwvaDM+Cgo8YnIvPgoKPGhyIHN0eWxlPSJib3JkZXI6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjQ0NDOyIvPgoKPGJyLz4KCjxoMj48c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5SZWxhdGlvbmFsIERhdGFiYXNlIFNvbHV0aW9uPC9zcGFuPjwvaDI+Cgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodDsiPjxpbWcgc3JjPSJJTUFHRVMvUkRCTVNfR3JvdXAucG5nIiB3aWR0aD0iMjUwcHgiLz48L2Rpdj4KCi0gIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPkRhdGEgcGVyc2lzdGVuY2U6IGhpc3RvcmljYWwgYW5kIGN1cnJlbnQgbmVlZHM8L3NwYW4+Ci0gIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPk1haW50ZW5hbmNlL2h5Z2llbmU6IGFkaGVyZW5jZSB0byB0eXBlczwvc3Bhbj4KCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0OyI+PGltZyBzcmM9IklNQUdFUy9wb3N0Z3Jlc3FsLmpwZWciIHdpZHRoPSIxMDBweCIvPjwvZGl2PgoKLSAjIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+U3RvcmFnZSBlZmZpY2llbmN5OiBub3JtYWxpemF0aW9uIHJlZHVjZXMgcmVwZXRpdGlvbiBvZiBkYXRhPC9zcGFuPgotICMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5DZW50cmFsaXphdGlvbjogbXVsdGlwbGUgdXNlciBlbnZpcm9ubWVudCBhbmQgc2VjdXJpdHk8L3NwYW4+Ci0gIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlNjYWxhYmlsaXR5OiBub3QgbGltaXRlZCB0byBsb2NhbCByZXNvdXJjZXM8L3NwYW4+Cgo8YnIvPgo8YnIvPgoKPGhyIHN0eWxlPSJib3JkZXI6IG5vbmU7IGhlaWdodDogMXB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjQ0NDOyIvPgoKPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxhIGhyZWY9Imh0dHBzOi8vaHViLmRvY2tlci5jb20vci9wYXJmYWl0Zy9lbnZpcm9ubWVudF9kYi90YWdzIj48aW1nIHNyYz0iSU1BR0VTL2RvY2tlcl9wZ3NxbC5wbmciIHdpZHRoPSIxNTBweCIvPjwvYT48L2Rpdj4KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+VXNlIENhc2U6IEVudmlyb25tZW50IERhdGFiYXNlPC9hPjwvc3Bhbj4KCjxoMz48c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz48aT5IdW1hbiBJbXBhY3Qgb24gdGhlIEdsb2JhbCBhbmQgTG9jYWwgQmlvc3BoZXJlIGFuZCBDbGltYXRlPC9pPjwvc3Bhbj48L2gzPgo8ZGl2IHN0eWxlPSJmbG9hdDpjZW50ZXI7IHRleHQtYWxpZ246Y2VudGVyOyI+PGltZyBzcmM9IklNQUdFUy9lbnZfZGF0YS5wbmciLz48L2Rpdj4KPGJyLz4KCjxociBzdHlsZT0iYm9yZGVyOiBub25lOyBoZWlnaHQ6IDFweDsgYmFja2dyb3VuZC1jb2xvcjogI0NDQzsiLz4KCmBgYHtyfQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoa2FibGVFeHRyYSkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh4dHMpKQoKc2VhYm9ybl9wYWxldHRlIDwtIGMoIiM0QzcyQjAiLCAiI0REODQ1MiIsICIjNTVBODY4IiwgIiNDNDRFNTIiLCAiIzgxNzJCMyIsICIjOTM3ODYwIiwgCiAgICAgICAgICAgICAgICAgICAgICIjREE4QkMzIiwgIiM4QzhDOEMiLCAiI0NDQjk3NCIsICIjNjRCNUNEIiwgIiM0QzcyQjAiLCAiI0REODQ1MiIpCmBgYAoKIyMgQ29ubmVjdGlvbgoKLSAjIyMjIENlbnRyYWxpemVkIGxvY2F0aW9uIGZvciBtdWx0aXBsZSB1c2VycwotICMjIyMgV2VsbCBtYWludGFpbmVkIEFQSSBtb2R1bGVzCi0gIyMjIyBNdWx0aXBsZSBiYWNrZW5kIGNvbm5lY3Rpb24gdHlwZXMKCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz48YSBocmVmPSJodHRwczovL3d3dy5wb3N0Z3Jlc3FsLm9yZy9mdHAvb2RiYy92ZXJzaW9ucy9zcmMvIiB0YXJnZXQ9ImJsYW5rIj5PREJDPC9hPjwvc3Bhbj4KCmBgYHtyfQpsaWJyYXJ5KERCSSkKbGlicmFyeShvZGJjKQoKY29ubiA8LSBkYkNvbm5lY3Qob2RiYzo6b2RiYygpLCBkcml2ZXI9IlBvc3RncmVTUUwgVW5pY29kZSIsIAogICAgICAgICAgICAgICAgICBzZXJ2ZXI9ImxvY2FsaG9zdCIsIGRhdGFiYXNlPSJlbnZpcm9ubWVudCIsCiAgICAgICAgICAgICAgICAgIHVpZD0icG9zdGdyZXMiLCBwd2Q9ImVudjE5IiwgcG9ydD02NDMyKQpkYkxpc3RUYWJsZXMoY29ubikKCmRiRGlzY29ubmVjdChjb25uKQpgYGAKCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz48YSBocmVmPSJodHRwczovL2pkYmMucG9zdGdyZXNxbC5vcmcvZG93bmxvYWQuaHRtbCIgdGFyZ2V0PSJibGFuayI+SkRCQzwvYT48L3NwYW4+CgpgYGB7cn0KbGlicmFyeShySmF2YSkKbGlicmFyeShSSkRCQykKCmRydiA8LSBKREJDKCJvcmcucG9zdGdyZXNxbC5Ecml2ZXIiLAogICAgICAgICAgICIvdXNyL2xpYi9qdm0vamF2YS04LW9yYWNsZS9saWIvcG9zdGdyZXNxbC00Mi4yLjIuamFyIikKY29ubiA8LSBkYkNvbm5lY3QoZHJ2LCAiamRiYzpwb3N0Z3Jlc3FsOi8vbG9jYWxob3N0OjY0MzIvZW52aXJvbm1lbnQiLCAicG9zdGdyZXMiLCAiZW52MTkiKQpkYkxpc3RUYWJsZXMoY29ubiwgc2NoZW1hPSJwdWJsaWMiKQoKZGJEaXNjb25uZWN0KGNvbm4pCmBgYAoKCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz48YSBocmVmPSJodHRwczovL3JkcnIuaW8vY3Jhbi9SUG9zdGdyZVNRTC8iIHRhcmdldD0iYmxhbmsiPlItUG9zdGdyZXMgQVBJPC9hPjwvc3Bhbj4KCmBgYHtyfQpsaWJyYXJ5KFJQb3N0Z3JlU1FMKQoKY29ubiA8LSBkYkNvbm5lY3QoUlBvc3RncmVTUUw6OlBvc3RncmVTUUwoKSwgaG9zdD0ibG9jYWxob3N0IiwgZGJuYW1lPSJlbnZpcm9ubWVudCIsCiAgICAgICAgICAgICAgICAgIHVzZXI9InBvc3RncmVzIiwgcGFzc3dvcmQ9ImVudjE5IiwgcG9ydD02NDMyKQpkYkxpc3RUYWJsZXMoY29ubikKYGBgCgo8aHIgc3R5bGU9ImJvcmRlcjogbm9uZTsgaGVpZ2h0OiAxcHg7IGJhY2tncm91bmQtY29sb3I6ICNDQ0M7Ii8+CgojIyBJbXBvcnQgJiBNYW5hZ2UgRGF0YQoKLSAjIyMjIFNlYW1sZXNzIGJ1bGsgdGV4dCBmaWxlIGltcG9ydAotICMjIyMgRGF0YSBoeWdpZW5lIHN1cHBvcnQKLSAjIyMjIEFubm90YXRlIHNvdXJjZXMgd2l0aCBjb21tZW50cwoKYGBge3NxbCwgZXZhbD1GQUxTRX0KQ1JFQVRFIFRBQkxFIGdsb2JhbF90ZW1wZXJhdHVyZSAoCiAgIGlkIFNFUklBTCBOT1QgTlVMTCBQUklNQVJZIEtFWSwKICAgeWVhciBJTlQsCiAgIHBlcmlvZCBWQVJDSEFSKDUwKSwKICAgZ2xvYmFsX21lYW4gTlVNRVJJQyg1LDIpCik7CgpcY29weSBnbG9iYWxfdGVtcGVyYXR1cmUgKHllYXIsIHBlcmlvZCwgZ2xvYmFsX21lYW4pIEZST00gJy9ob21lL3BhcmZhaXRnL0RhdGFiYXNlcy9TUUxfU2VydmVyL0VOVklST05NRU5UL2dsb2JhbF90ZW1wZXJhdHVyZS5jc3YnIERFTElNSVRFUiAnLCcgQ1NWIEhFQURFUjsKCkNPTU1FTlQgT04gVEFCTEUgZ2xvYmFsX3RlbXBlcmF0dXJlIElTICdTb3VyY2U6IE5BU0EgLSBDb21iaW5lZCBMYW5kLVN1cmZhY2UgQWlyIGFuZCBTZWEtU3VyZmFjZSBXYXRlciBUZW1wZXJhdHVyZSBBbm9tYWxpZXMgKExhbmQtT2NlYW4gVGVtcGVyYXR1cmUgSW5kZXgsIExPVEkpIChodHRwczovL2RhdGEuZ2lzcy5uYXNhLmdvdi9naXN0ZW1wLyknOwpgYGAKCmBgYHtzcWwgY29ubmVjdGlvbj1jb25uLCBvdXRwdXQudmFyID0gImNvbW1lbnRzX2RmIn0KU0VMRUNUIHJlbG5hbWUgYXMgdGFibGUsIG9ial9kZXNjcmlwdGlvbihvaWQpIGFzIGNvbW1lbnQKRlJPTSBwZ19jbGFzcwpXSEVSRSByZWxraW5kID0gJ3InCiAgQU5EIG9ial9kZXNjcmlwdGlvbihvaWQpIElTIE5PVCBOVUxMCiAgT1JERVIgQlkgcmVsbmFtZQpgYGAKCmBgYHtyfQprYWJsZV9zdHlsaW5nKGthYmxlKGNvbW1lbnRzX2RmKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgo8aHIgc3R5bGU9ImJvcmRlcjogbm9uZTsgaGVpZ2h0OiAxcHg7IGJhY2tncm91bmQtY29sb3I6ICNDQ0M7Ii8+CgojIyBBbmFseXplIERhdGEKCi0gIyMjIyBEZWNhbGFyYXRpdmUgU1FMIHRvIGludGVyYWN0IHdpdGggbWFueSBkYXRhCi0gIyMjIyBMZXZlcmFnZSBxdWVyeSBlbmdpbmUgZm9yIGRhdGEgcHJvY2Vzc2luZwotICMjIyMgUmVhZGFibGUsIG1haW50YWluYWJsZSwgdHJhbmZlcmFibGUKCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48YSBocmVmPSJodHRwczovL3d3dy5lc3JsLm5vYWEuZ292L2dtZC9vYm9wL21sby8iIGJsYW5rPSJ0YXJnZXQiPjxpbWcgc3JjPSJJTUFHRVMvbm9hYS5wbmciIGhlaWdodD0iNTBweCIvPjwvYT48L2Rpdj4KPGRpdiBzdHlsZT0iZmxvYXQ6cmlnaHQiPjxhIGhyZWY9Imh0dHBzOi8vd3d3LmVpYS5nb3YvdG90YWxlbmVyZ3kvZGF0YS9tb250aGx5LyIgYmxhbmsgPSJ0YWdldCI+PGltZyBzcmM9IklNQUdFUy9kb2VfZWlhLnBuZyIgaGVpZ2h0PSI1MHB4Ii8+PC9hPjwvZGl2PgoKCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5EZXBhcnRtZW50IG9mIEVuZXJneSdzIEVJQTogTW9udGhseSBFbmVyZ3kgUmV2aWV3ICY8L3NwYW4+CiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5OT0FBIE1hdW5hIExvYSwgSGF3YWlpIE9ic2VydmF0b3J5IC0gQ2FyYm9uIFBQTTwvc3Bhbj4KCmBgYHtzcWwgY29ubmVjdGlvbj1jb25uLCBvdXRwdXQudmFyID0gImFnZ19kZiJ9ClNFTEVDVCBwLmRhdGVfeWVhciwKICAgICAgIHN1bShwLmF2ZXJhZ2VfcHBtKSBhcyBjYXJib25fcHBtX3RvdGFsLCAKICAgICAgIGF2ZyhwLmF2ZXJhZ2VfcHBtKSBhcyBjYXJib25fcHBtX21lYW4sIAogICAgICAgc3VtKGMuZW5lcmd5X2NvbnN1bWVkKSBhcyBidHVfY29uc3VtZWRfdG90YWwsCiAgICAgICBhdmcoYy5lbmVyZ3lfY29uc3VtZWQpIGFzIGJ0dV9jb25zdW1lZF9tZWFuLAogICAgICAgc3VtKGUuZW5lcmd5X2NvMikgYXMgY28yX2VtaXNzaW9uc190b3RhbCwKICAgICAgIGF2ZyhlLmVuZXJneV9jbzIpIGFzIGNvMl9lbWlzc2lvbnNfbWVhbgogIEZST00gcHBtX21vbnRoIHAKICBKT0lOIGNvbnN1bXB0aW9uIGMgCiAgICBPTiBwLmRhdGVfeWVhciA9IGMuZGF0ZV95ZWFyIEFORCBwLmRhdGVfbW9udGggPSBjLmRhdGVfbW9udGggYW5kIGMubXNuID0gJ1RYUkNCVVMnCiAgSk9JTiB1c19jbzJfZW1pc3Npb25zIGUKICAgIE9OIHAuZGF0ZV95ZWFyID0gZS5kYXRlX3llYXIgQU5EIHAuZGF0ZV9tb250aCA9IGUuZGF0ZV9tb250aCBhbmQgZS5tc24gPSAnVEVUQ0VVUycKR1JPVVAgQlkgcC5kYXRlX3llYXIKT1JERVIgQlkgcC5kYXRlX3llYXIKYGBgCgpgYGB7cn0Ka2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGFnZ19kZiwgMTApKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgpgYGB7c3FsIGNvbm5lY3Rpb249Y29ubiwgb3V0cHV0LnZhciA9ICJtZXRyaWNzX2RmIn0KU0VMRUNUIHAuZGF0ZV95ZWFyLCBwLmRhdGVfbW9udGgsIENPTkNBVChwLmRhdGVfeWVhciwgJy0nLCBwLmRhdGVfbW9udGgsICctMDEnKTo6ZGF0ZSBhcyBkYXRlX2RheSwKICAgICAgICAgICAgICAgcC5hdmVyYWdlX3BwbSBhcyAiY2FyYm9uIHBwbSIsIGMuZW5lcmd5X2NvbnN1bWVkIGFzICJidHUgY29uc3VtZWQiLCBlLmVuZXJneV9jbzIgYXMgImNvMiBlbWlzc2lvbnMiCiAgRlJPTSBwcG1fbW9udGggcAogIEpPSU4gY29uc3VtcHRpb24gYyAKICAgIE9OIHAuZGF0ZV95ZWFyID0gYy5kYXRlX3llYXIgQU5EIHAuZGF0ZV9tb250aCA9IGMuZGF0ZV9tb250aCBhbmQgYy5tc24gPSAnVFhSQ0JVUycKICBKT0lOIHVzX2NvMl9lbWlzc2lvbnMgZQogICAgT04gcC5kYXRlX3llYXIgPSBlLmRhdGVfeWVhciBBTkQgcC5kYXRlX21vbnRoID0gZS5kYXRlX21vbnRoIGFuZCBlLm1zbiA9ICdURVRDRVVTJwogIE9SREVSIEJZIHAuZGF0ZV95ZWFyLCBwLmRhdGVfbW9udGgKYGBgCgpgYGB7ciBmaWcxYSwgZmlnLmhlaWdodCA9IDcsIGZpZy53aWR0aCA9IDEzLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KbWV0cmljX3RzIDwtIHh0cyh4PW1ldHJpY3NfZGZbYygiY2FyYm9uIHBwbSIsICJidHUgY29uc3VtZWQiLCAiY28yIGVtaXNzaW9ucyIpXSwgCiAgICAgICAgICAgICAgICAgb3JkZXIuYnk9bWV0cmljc19kZiRkYXRlX2RheSkKCnBsb3QobWV0cmljX3RzLCBtYWluID0gIkVuZXJneSBhbmQgQ2FyYm9uIFBQTSBNZXRyaWNzIiwKICAgICBsZWdlbmQubG9jPSJib3R0b21yaWdodCIsIAogICAgIGNvbCA9IHNlYWJvcm5fcGFsZXR0ZVsxOjNdLAogICAgIHlheGlzLnJpZ2h0PUZBTFNFLAogICAgIGF4ZXM9RkFMU0UsCiAgICAgbHdkPTEsCiAgICAgY2V4Lm1haW49MywKICAgICBtYWpvci50aWNrcz0ieWVhcnMiLAogICAgIG1ham9yLmZvcm1hdD0iJVkiLAogICAgIG1pbm9yLmZvcm1hdD0iJVkiLAogICAgIGdyaWQudGlja3MubHR5PTEpCmBgYAoKIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlNlYXNvbmFsIERlY29tcG9zaXRpb248L3NwYW4+CgpgYGB7ciAgZmlnMWIsIGZpZy5oZWlnaHQgPSA3LCBmaWcud2lkdGggPSAxMywgZmlnLmFsaWduID0gImNlbnRlciJ9CmNhcmJvbnBwbV90cyA8LSB0cyhtZXRyaWNzX2RmJGBjYXJib24gcHBtYCwgc3RhcnQ9YygxOTczLCAxKSwgZnJlcXVlbmN5PTEyKQpjYXJib25wcG1fc3RsIDwtIHN0bChjYXJib25wcG1fdHMsIHMud2luZG93PSJwZXJpb2RpYyIpCgpwbG90KGNhcmJvbnBwbV9zdGwsIGNvbCA9IHNlYWJvcm5fcGFsZXR0ZVsxXSwgCiAgICAgbWFpbj0iU2Vhc29uYWwgRGVjb21wb3NpdGlvbiBvZiBHbG9iYWwgQ2FyYm9uIFBQTSIpCmBgYAoKYGBge3IgIGZpZzFjLCBmaWcuaGVpZ2h0ID0gNywgZmlnLndpZHRoID0gMTMsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpjb25zdW1lZF90cyA8LSB0cyhtZXRyaWNzX2RmJGBidHUgY29uc3VtZWRgLCBzdGFydD1jKDE5NzMsIDEpLCBmcmVxdWVuY3k9MTIpCmNvbnN1bWVkX3N0bCA8LSBzdGwoY29uc3VtZWRfdHMsIHMud2luZG93PSJwZXJpb2RpYyIpCgpwbG90KGNvbnN1bWVkX3N0bCwgY29sID0gc2VhYm9ybl9wYWxldHRlWzJdLCAKICAgICBtYWluPSJTZWFzb25hbCBEZWNvbXBvc2l0aW9uIG9mIFUuUy4gRW5lcmd5IENvbnN1bXB0aW9uIikKYGBgCgpgYGB7ciBmaWcxZCwgZmlnLmhlaWdodCA9IDcsIGZpZy53aWR0aCA9IDEzLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KZW1pc3Npb25zX3RzIDwtIHRzKG1ldHJpY3NfZGYkYGNvMiBlbWlzc2lvbnNgLCBzdGFydD1jKDE5NzMsIDEpLCBmcmVxdWVuY3k9MTIpCmVtaXNzaW9uc19zdGwgPC0gc3RsKGVtaXNzaW9uc190cywgcy53aW5kb3c9InBlcmlvZGljIikKCnBsb3QoZW1pc3Npb25zX3N0bCwgY29sID0gc2VhYm9ybl9wYWxldHRlWzNdLAogICAgIG1haW4gPSAiU2Vhc29uYWwgRGVjb21wb3NpdGlvbiBvZiBVLlMuIENPMiBFbWlzc2lvbnMiKQpgYGAKCmBgYHtyfQpzcWwgPC0gIldJVEggYzEgQVMKICAgICAgICAgICAoU0VMRUNUIENPTkNBVCgoZGF0ZV95ZWFyLzEwKTo6aW50ICogMTAsICdzJykgYXMgZGVjYWRlLCAKICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoZGVzY3JpcHRpb24sICdQcmltYXJ5IEVuZXJneSBDb25zdW1lZCBieSB0aGUgJywgJycpIEFTIFwiU2VjdG9yXCIsCiAgICAgICAgICAgICAgICAgICBTVU0oZW5lcmd5X2NvbnN1bWVkKSBBUyBcIlByaW1hcnkgRW5lcmd5IENvbnN1bWVkXCIKICAgICAgICAgICAgRlJPTSBjb25zdW1wdGlvbgogICAgICAgICAgICBXSEVSRSBtc24gSU4gKCdUWElDQlVTJywgJ1RYUkNCVVMnLCAnVFhBQ0JVUycsICdUWENDQlVTJywgJ1RYRUlCVVMnKQogICAgICAgICAgICBHUk9VUCBCWSBDT05DQVQoKGRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpLCBkZXNjcmlwdGlvbgogICAgICAgICAgICksIGMyIEFTCiAgICAgICAgICAgKFNFTEVDVCBDT05DQVQoKGRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpIGFzIGRlY2FkZSwgCiAgICAgICAgICAgICAgICAgICBSRVBMQUNFKGRlc2NyaXB0aW9uLCAnVG90YWwgRW5lcmd5IENvbnN1bWVkIGJ5IHRoZSAnLCAnJykgQVMgXCJTZWN0b3JcIiwKICAgICAgICAgICAgICAgICAgIFNVTShlbmVyZ3lfY29uc3VtZWQpIEFTIFwiVG90YWwgRW5lcmd5IENvbnN1bWVkXCIKICAgICAgICAgICAgRlJPTSBjb25zdW1wdGlvbgogICAgICAgICAgICBXSEVSRSBtc24gSU4gKCdURUNDQlVTJywgJ1RFQUNCVVMnLCAnVEVJQ0JVUycsICdURVJDQlVTJykKICAgICAgICAgICAgR1JPVVAgQlkgQ09OQ0FUKChkYXRlX3llYXIvMTApOjppbnQgKiAxMCwgJ3MnKSwgZGVzY3JpcHRpb24KICAgICAgICAgICApCgogICAgICAgIFNFTEVDVCBjMS5kZWNhZGUsIGMxLlwiU2VjdG9yXCIsIGMxLlwiUHJpbWFyeSBFbmVyZ3kgQ29uc3VtZWRcIiwgYzIuXCJUb3RhbCBFbmVyZ3kgQ29uc3VtZWRcIgogICAgICAgIEZST00gYzEKICAgICAgICBMRUZUIEpPSU4gYzIKICAgICAgICAgICAgT04gYzEuXCJTZWN0b3JcIiA9IGMyLlwiU2VjdG9yXCIgQU5EIGMxLmRlY2FkZSA9IGMyLmRlY2FkZQogICAgICAgIE9SREVSIEJZIGMxLmRlY2FkZSwgYzEuXCJTZWN0b3JcIgogICAgICAgIgoKY29uc3VtZWRfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwoY29uc3VtZWRfZGYpKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9kb2VfZWlhLnBuZyIgd2lkdGg9Ijc1cHgiLz48L2Rpdj4KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPkNvbnN1bXB0aW9uIGFuZCBDTzIgRW1pc3Npb25zPC9zcGFuPgoKCmBgYHtyIGZpZzFlLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwbG90X21hdCA8LSB3aXRoKHN1YnNldChjb25zdW1lZF9kZiwgU2VjdG9yICE9ICdFbGVjdHJpYyBQb3dlciBTZWN0b3InKSwKICAgICAgICAgICAgICAgICB0YXBwbHkoYFRvdGFsIEVuZXJneSBDb25zdW1lZGAsIGxpc3QoZGVjYWRlLCBgU2VjdG9yYCksIHN1bSkpCgpwYXIobWFyPWMoNSwgNSwgMiwgMSkpCmJhcnBsb3QocGxvdF9tYXQsIG1haW49IlUuUy4gVG90YWwgRW5lcmd5IENvbnN1bXB0aW9uIGJ5IFNlY3RvciIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6OF0sIHlsaW09YygwLCA4RTUpLCB4YXh0PSJuIiwgeWF4dD0ibiIsIGJlc2lkZT1UUlVFKQoKYXhpcyhzaWRlPTEsIGF0PWMoNSwgMTQsIDIzLCAzMiksIGxhYmVscz1jb2xuYW1lcyhwbG90X21hdCksIHBhZGo9MC45LCBwb3M9YygwLDApKQpheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVsxOjhdLCBuY29sPTgpCmBgYAoKYGBge3IgZmlnMWYsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSAxNSwgZmlnLmFsaWduID0gImNlbnRlciJ9CnBsb3RfbWF0IDwtIHdpdGgoY29uc3VtZWRfZGYsIHRhcHBseShgUHJpbWFyeSBFbmVyZ3kgQ29uc3VtZWRgLCBsaXN0KGRlY2FkZSwgYFNlY3RvcmApLCBzdW0pKQoKcGFyKG1hcj1jKDUsIDUsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIFByaW1hcnkgRW5lcmd5IENvbnN1bXB0aW9uIGJ5IFNlY3RvciIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6OF0sIHlsaW09YygwLCA5RTUpLCB4YXh0PSJuIiwgeWF4dD0ibiIsIGJlc2lkZT1UUlVFKQoKYXhpcyhzaWRlPTEsIGF0PWMoNSwgMTQsIDIzLCAzMiwgNDEpLCBsYWJlbHM9Y29sbmFtZXMocGxvdF9tYXQpLCBwYWRqPTAuOSwgcG9zPWMoMCwwKSkKYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSwgbGFzPTEpCmxlZ2VuZCgidG9wIiwgcm93Lm5hbWVzKHBsb3RfbWF0KSwgZmlsbD1zZWFib3JuX3BhbGV0dGVbMTo4XSwgbmNvbD04KQpgYGAKCmBgYHtyfQpzcWwgPC0gIlNFTEVDVCBDT05DQVQoKGRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpIGFzIGRlY2FkZSwgCiAgICAgICAgICAgICAgIFJFUExBQ0UoUkVQTEFDRShkZXNjcmlwdGlvbiwgJ1RvdGFsIEVuZXJneSAnLCAnJyksICcgQ08yIEVtaXNzaW9ucycsICcnKSBBUyBcIlNlY3RvclwiLAogICAgICAgICAgICAgICBTVU0oZW5lcmd5X2NvMikgQVMgXCJUb3RhbCBDTzIgRW1pc3Npb25zXCIKICAgICAgICBGUk9NIHVzX2NvMl9lbWlzc2lvbnMKICAgICAgICBXSEVSRSBtc24gSU4gKCdURVJDRVVTJywgJ1RFQ0NFVVMnLCAnVEVBQ0VVUycsICdUWEVJRVVTJykKICAgICAgICBHUk9VUCBCWSBkYXRlX3llYXIsIGRlc2NyaXB0aW9uCiAgICAgICAgT1JERVIgQlkgZGF0ZV95ZWFyLCBkZXNjcmlwdGlvbgogICAgICAgIgoKZW1pc3Npb25zX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGVtaXNzaW9uc19kZikpLAogICAgICAgICAgICAgIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQpgYGAKCmBgYHtyIGZpZzFnLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwbG90X21hdCA8LSB3aXRoKGVtaXNzaW9uc19kZiwgdGFwcGx5KGBUb3RhbCBDTzIgRW1pc3Npb25zYCwgbGlzdChkZWNhZGUsIGBTZWN0b3JgKSwgc3VtKSkKCnBhcihtYXI9Yyg1LCA4LCAyLCAxKSkKYmFycGxvdChwbG90X21hdCwgbWFpbj0iVS5TLiBDTzIgRW1pc3Npb25zIGJ5IFNlY3RvciIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6NV0sIHlsaW09YygwLCA2RTQpLCB4YXh0PSJuIiwgeWF4dD0ibiIsIGJlc2lkZT1UUlVFKQoKYXhpcyhzaWRlPTEsIGF0PWMoMywgOSwgMTUsIDIxKSswLjUsIGxhYmVscz1jb2xuYW1lcyhwbG90X21hdCksIHBhZGo9MC45LCBwb3M9YygwLDApKQpheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKbGVnZW5kKCJ0b3AiLCByb3cubmFtZXMocGxvdF9tYXQpLCBmaWxsPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCBuY29sPTUpCmBgYAoKCmBgYHtyfQpzcWwgPC0gIldJVEggc3ViIEFTCiAgICAgICAgICAoU0VMRUNUIENPTkNBVCgoZGF0ZV95ZWFyLzEwKTo6aW50ICogMTAsICdzJykgYXMgZGVjYWRlLCAKICAgICAgICAgICAgICAgICAgUkVQTEFDRSgkMSwgJyUnLCAnJykgYXMgc2VjdG9yLAogICAgICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSRVBMQUNFKGRlc2NyaXB0aW9uLCAnQ29tbWVyY2lhbCBTZWN0b3IgQ08yIEVtaXNzaW9ucycsICcnKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnUmVzaWRlbnRpYWwgU2VjdG9yIENPMiBFbWlzc2lvbnMnLCAnJwogICAgICAgICAgICAgICAgICAgICAgICksICdUcmFuc3BvcnRhdGlvbiBTZWN0b3IgQ08yIEVtaXNzaW9ucycsICcnKSwKICAgICAgICAgICAgICAgICAgJyAnLCAnXG4nKSAgQVMgXCJUeXBlXCIsCiAgICAgICAgICAgICAgICAgZW5lcmd5X2NvMgogICAgICAgICAgIEZST00gdXNfY28yX2VtaXNzaW9ucwogICAgICAgICAgIFdIRVJFIGRlc2NyaXB0aW9uIExJS0UgJDIpCgogICAgICAgU0VMRUNUIGRlY2FkZSwgc2VjdG9yLCBcIlR5cGVcIiwgCiAgICAgICAgICAgICAgU1VNKGVuZXJneV9jbzIpIEFTIFwiVG90YWwgQ08yIEVtaXNzaW9uc1wiCiAgICAgICBGUk9NIHN1YgogICAgICAgR1JPVVAgQlkgZGVjYWRlLCBzZWN0b3IsIFwiVHlwZVwiCiAgICAgICBPUkRFUiBCWSBkZWNhZGUsIHNlY3RvciwgXCJUeXBlXCIKICAgICAgICIKCnBhcmFtcyA8LSBwYXN0ZTAoYygiJVRyYW5zcG9ydGF0aW9uIiwgIiVSZXNpZGVudGlhbCIsICIlQ29tbWVyY2lhbCIpLCAiIFNlY3RvciUiKQplbWlzc2lvbnNfdHlwZV9kZiA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkocGFyYW1zLCBmdW5jdGlvbihwKSBkYkdldFF1ZXJ5KGNvbm4sIHNxbCwgcGFyYW09bGlzdChwLCBwKSkpKQpgYGAKCmBgYHtyIGZpZzFoLCBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQoKb3V0cHV0IDwtIGJ5KGVtaXNzaW9uc190eXBlX2RmLCBlbWlzc2lvbnNfdHlwZV9kZiRzZWN0b3IsIGZ1bmN0aW9uKHN1YikgewogIHBsb3RfbWF0IDwtIHdpdGgoc3ViLCB0YXBwbHkoYFRvdGFsIENPMiBFbWlzc2lvbnNgLCBsaXN0KGRlY2FkZSwgYFR5cGVgKSwgc3VtKSkKCiAgYmFycGxvdChwbG90X21hdCwgbWFpbj1wYXN0ZSgiVS5TLiBDTzIgRW1pc3Npb25zIGJ5Iiwgc3ViJHNlY3RvcltbMV1dKSwgY2V4Lm1haW49MS41LAogICAgICAgICAgY29sPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCB5bGltPWMoMCwgY2VpbGluZyhtYXgocGxvdF9tYXQsIG5hLnJtPVRSVUUpLzFFNCkgKiAxRTQpLCAKICAgICAgICAgIHhheHQ9Im4iLCB5YXh0PSJuIiwgYmVzaWRlPVRSVUUpCiAgCiAgYXhpcyhzaWRlPTEsIGF0PWMoMyw5LDE1LDIxLDI3LDMzLDM5LDQ1LDUxLDU3LDYzKVtzZXFfYWxvbmcoY29sbmFtZXMocGxvdF9tYXQpKV0gKyAwLjUsCiAgICAgICBsYWJlbHM9Y29sbmFtZXMocGxvdF9tYXQpLCBwYWRqPTAuOSwgcG9zPWMoMCwwKSkKICBheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpLCBsYXM9MSkKICBsZWdlbmQoInRvcCIsIHJvdy5uYW1lcyhwbG90X21hdCksIGZpbGw9c2VhYm9ybl9wYWxldHRlWzE6NV0sIG5jb2w9NSkKfSkKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy9kb2VfZWlhLnBuZyIgd2lkdGg9Ijc1cHgiLz48L2Rpdj4KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlJlbmV3YWJsZSBFbmVyZ3k6IFByb2R1Y3Rpb24gYW5kIENvbnN1bXB0aW9uPC9zcGFuPgoKYGBge3J9CnNxbCA8LSAiU0VMRUNUIGVuZXJneV90eXBlLAogICAgICAgICAgICAgICBkYXRlLAogICAgICAgICAgICAgICBTVU0ocHJvZHVjdGlvbikgQVMgcHJvZHVjdGlvbiwKICAgICAgICAgICAgICAgU1VNKGNvbnN1bXB0aW9uKSBBUyBjb25zdW1wdGlvbgogICAgICAgIEZST00gdXNfcmVuZXdhYmxlX2VuZXJneQogICAgICAgIEdST1VQIEJZIGVuZXJneV90eXBlLAogICAgICAgICAgICAgICAgIGRhdGUKICAgICAgICBPUkRFUiBCWSBlbmVyZ3lfdHlwZSwKICAgICAgICAgICAgICAgICBkYXRlCiAgICAgICAiCgpyZW5ld2FibGVfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwocmVuZXdhYmxlX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgcmVuZXdhYmxlX2ZpZywgZmlnLmhlaWdodCA9IDE1LCBmaWcud2lkdGggPSAxNSwgZmlnLmFsaWduID0gImNlbnRlciJ9CgpwYXIobWZyb3c9YygzLDMpLCBtYXI9Yyg1LCA1LCAyLCAxKSkKb3V0cHV0IDwtIGJ5KHJlbmV3YWJsZV9kZiwgcmVuZXdhYmxlX2RmJGVuZXJneV90eXBlLCBmdW5jdGlvbihzdWIpIHsKICAKICBtZXRyaWNfdHMgPC0geHRzKHg9c3ViW2MoInByb2R1Y3Rpb24iLCAiY29uc3VtcHRpb24iKV0sIAogICAgICAgICAgICAgICAgICAgb3JkZXIuYnk9c3ViJGRhdGUpCiAgCiAgcHJpbnQocGxvdChtZXRyaWNfdHMsIG1haW4gPSBzdWIkZW5lcmd5X3R5cGVbMV0sCiAgICAgICAgICAgICBsZWdlbmQubG9jPSJib3R0b21yaWdodCIsIAogICAgICAgICAgICAgY29sID0gc2VhYm9ybl9wYWxldHRlWzE6M10sCiAgICAgICAgICAgICB5YXhpcy5yaWdodD1GQUxTRSwKICAgICAgICAgICAgIGF4ZXM9RkFMU0UsCiAgICAgICAgICAgICBsd2Q9MSwKICAgICAgICAgICAgIGNleC5tYWluPTMsCiAgICAgICAgICAgICBtYWpvci50aWNrcz0ieWVhcnMiLAogICAgICAgICAgICAgbWFqb3IuZm9ybWF0PSIlWSIsCiAgICAgICAgICAgICBtaW5vci5mb3JtYXQ9IiVZIiwKICAgICAgICAgICAgIGdyaWQudGlja3MubHR5PTEpKQogIAp9KQpgYGAKCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48YSBocmVmPSJodHRwczovL3dhdGVyZGF0YS51c2dzLmdvdi9ud2lzL2d3Ij48aW1nIHNyYz0iSU1BR0VTL3VzZ3MucG5nIiB3aWR0aD0iNzVweCIvPjwvYT48L2Rpdj4KCiMjIyA8c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5VLlMuIEdlb2xvZ2ljYWwgU3VydmV5IC0gR3JvdW5kd2F0ZXIgV2VsbCBEZXB0aCBEYXRhPC9zcGFuPgpgYGB7cn0Kc3FsIDwtICJTRUxFQ1QgZy55ZWFyLCBnLm1vbnRoLAogICAgICAgICAgICAgICBhdmcoZy5tZWFuX3ZhbHVlKSBhcyBtZWFuX3ZhbHVlCiAgICAgICAgRlJPTSBncm91bmR3YXRlciBnCiAgICAgICAgV0hFUkUgZy55ZWFyIEJFVFdFRU4gMTk5MCBBTkQgMjAxOQogICAgICAgIEdST1VQIEJZIGcueWVhciwgZy5tb250aCIKCmdyb3VuZHdhdGVyX2RmIDwtIGRiR2V0UXVlcnkoY29ubiwgc3FsKQoKa2FibGVfc3R5bGluZyhrYWJsZSh0YWlsKGdyb3VuZHdhdGVyX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgZmlnMywgZmlnLmhlaWdodCA9IDcsIGZpZy53aWR0aCA9IDE2LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KYm94cGxvdChtZWFuX3ZhbHVlIH4geWVhciwgZ3JvdW5kd2F0ZXJfZGYsIGNvbD1zZWFib3JuX3BhbGV0dGVbMToxMF0sIAogICAgICAgIG1haW49Ikdyb3VuZHdhdGVyIFdlbGwgRGVwdGggTWVhbiBWYWx1ZXMiLCBjZXgubWFpbj0xLjUpCmBgYAoKCmBgYHtyfQpzcWwgPC0gIldJVEggc2l0ZXMgQVMgKAogICAgICAgICAgICBTRUxFQ1QgQ09OQ0FUKGcueWVhciwgJy0nLCBnLm1vbnRoLCAnLTEnKTo6ZGF0ZSBBUyBcImRhdGVcIiwKICAgICAgICAgICAgICAgICAgIGcuc2l0ZV9uYW1lLAogICAgICAgICAgICAgICAgICAgQVZHKGcubWVhbl92YWx1ZSkgYXMgbWVhbl92YWx1ZQogICAgICAgICAgICBGUk9NIGdyb3VuZHdhdGVyIGcKICAgICAgICAgICAgV0hFUkUgZy55ZWFyIEJFVFdFRU4gMTk5MCBBTkQgMjAxOQogICAgICAgICAgICBHUk9VUCBCWSBDT05DQVQoZy55ZWFyLCAnLScsIGcubW9udGgsICctMScpOjpkYXRlLAogICAgICAgICAgICAgICAgICAgICBnLnNpdGVfbmFtZQogICAgICAgICksIGJvdHRvbSBBUyAoCiAgICAgICAgICAgIFNFTEVDVCBzaXRlX25hbWUsIEFWRyhtZWFuX3ZhbHVlKSBBUyBtZWFuX3ZhbHVlLCAnYm90dG9tX3NpdGVzJyBBUyBjYXRlZ29yeQogICAgICAgICAgICBGUk9NIHNpdGVzIAogICAgICAgICAgICBHUk9VUCBCWSBzaXRlX25hbWUKICAgICAgICAgICAgT1JERVIgQlkgQVZHKG1lYW5fdmFsdWUpIEFTQyBMSU1JVCA1CiAgICAgICAgKSwgdG9wIEFTICgKICAgICAgICAgICAgU0VMRUNUIHNpdGVfbmFtZSwgQVZHKG1lYW5fdmFsdWUpIEFTIG1lYW5fdmFsdWUsICd0b3Bfc2l0ZXMnIEFTIGNhdGVnb3J5CiAgICAgICAgICAgIEZST00gc2l0ZXMgCiAgICAgICAgICAgIEdST1VQIEJZIHNpdGVfbmFtZQogICAgICAgICAgICBPUkRFUiBCWSBBVkcobWVhbl92YWx1ZSkgREVTQyBMSU1JVCA1CiAgICAgICAgKQoKICAgICAgICBTRUxFQ1Qgc2l0ZXMuXCJkYXRlXCIsIHNpdGVzLm1lYW5fdmFsdWUsIHNpdGVzLnNpdGVfbmFtZSwgdG9wLmNhdGVnb3J5CiAgICAgICAgRlJPTSBzaXRlcwogICAgICAgIElOTkVSIEpPSU4gdG9wCiAgICAgICAgICAgIE9OIHNpdGVzLnNpdGVfbmFtZSA9IHRvcC5zaXRlX25hbWUKCiAgICAgICAgVU5JT04KCiAgICAgICAgU0VMRUNUIHNpdGVzLlwiZGF0ZVwiLCBzaXRlcy5tZWFuX3ZhbHVlLCBzaXRlcy5zaXRlX25hbWUsIGJvdHRvbS5jYXRlZ29yeQogICAgICAgIEZST00gc2l0ZXMKICAgICAgICBJTk5FUiBKT0lOIGJvdHRvbQogICAgICAgICAgICBPTiBzaXRlcy5zaXRlX25hbWUgPSBib3R0b20uc2l0ZV9uYW1lCgogICAgICAgIE9SREVSIEJZIGNhdGVnb3J5LCBzaXRlX25hbWUsIFwiZGF0ZVwiIgoKZ3JvdW5kd2F0ZXJfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgprYWJsZV9zdHlsaW5nKGthYmxlKGhlYWQoZ3JvdW5kd2F0ZXJfZGYpKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgpgYGB7ciBncm91bmR3YXRlcl9maWcsIGZpZy5oZWlnaHQgPSAyNSwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQoKZ3JvdW5kd2F0ZXJfZGYkeWVhciA8LSBmb3JtYXQoZ3JvdW5kd2F0ZXJfZGYkZGF0ZSwgIiVZIikKCnBhcihtZnJvdz1jKDUsMiksIG1hcj1jKDUsIDUsIDIsIDEpKQpvdXRwdXQgPC0gYnkoZ3JvdW5kd2F0ZXJfZGYsIGdyb3VuZHdhdGVyX2RmJHNpdGVfbmFtZSwgZnVuY3Rpb24oc3ViKSB7CiAgCiAgbWV0cmljX3RzIDwtIHh0cyh4PXN1YltjKCJtZWFuX3ZhbHVlIildLCAKICAgICAgICAgICAgICAgICAgIG9yZGVyLmJ5PXN1YiRkYXRlKQogIAogIHByaW50KHBsb3QobWV0cmljX3RzLCBtYWluID0gcGFzdGUoc3ViJHNpdGVfbmFtZVsxXSwgJzonLCBzdWIkY2F0ZWdvcnlbMV0pLAogICAgICAgICAgICAgbGVnZW5kLmxvYz0iYm90dG9tcmlnaHQiLCAKICAgICAgICAgICAgIGNvbCA9IHNlYWJvcm5fcGFsZXR0ZVsxOjNdLAogICAgICAgICAgICAgeWF4aXMucmlnaHQ9RkFMU0UsCiAgICAgICAgICAgICBheGVzPUZBTFNFLAogICAgICAgICAgICAgbHdkPTEsCiAgICAgICAgICAgICBjZXgubWFpbj0zLAogICAgICAgICAgICAgbWFqb3IudGlja3M9InllYXJzIiwKICAgICAgICAgICAgIG1ham9yLmZvcm1hdD0iJVkiLAogICAgICAgICAgICAgbWlub3IuZm9ybWF0PSIlWSIsCiAgICAgICAgICAgICBncmlkLnRpY2tzLmx0eT0xKSkKICAKfSkKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGEgaHJlZj0iaHR0cHM6Ly93d3cuZndzLmdvdi9lbmRhbmdlcmVkLyIgdGFyZ2V0PSJibGFuayI+PGltZyBzcmM9IklNQUdFUy91c19md3MucG5nIiB3aWR0aD0iNzVweCIvPjwvYT48L2Rpdj4KIyMjIFUuUy4gRldTIEVuZGFuZ2VyZWQgU3BlY2llcyBMaXN0CgpgYGB7cn0Kc3FsIDwtICJTRUxFQ1QgQ09OQ0FUKChmLmRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpIEFTIGRlY2FkZSwgCiAgICAgICAgICAgICAgIGYudGF4b25vbWljX2dyb3VwLAogICAgICAgICAgICAgICBmLmN1cnJlbnRfc3RhdHVzLAogICAgICAgICAgICAgICBjb3VudCgqKSBBUyBzcGVjaWVzX2NvdW50CiAgICAgICAgRlJPTSBmd3Nfc3BlY2llc195ZWFyIGYKICAgICAgICBXSEVSRSBmLmN1cnJlbnRfc3RhdHVzIElOICgnRW5kYW5nZXJlZCcsICdSZWNvdmVyeScsICdSZXNvbHZlZCBUYXhvbicsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdUaHJlYXRlbmVkJywgJ0V4dGluY3Rpb24nKQogICAgICAgIEdST1VQIEJZIENPTkNBVCgoZi5kYXRlX3llYXIvMTApOjppbnQgKiAxMCwgJ3MnKSwKICAgICAgICAgICAgICAgICBmLnRheG9ub21pY19ncm91cCwKICAgICAgICAgICAgICAgICBmLmN1cnJlbnRfc3RhdHVzCiAgICAgICAgT1JERVIgQlkgQ09OQ0FUKChmLmRhdGVfeWVhci8xMCk6OmludCAqIDEwLCAncycpLAogICAgICAgICAgICAgICAgIGYudGF4b25vbWljX2dyb3VwLAogICAgICAgICAgICAgICAgIGYuY3VycmVudF9zdGF0dXMiCgpmd3NfZGYgPC0gZGJHZXRRdWVyeShjb25uLCBzcWwpCgprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwoZndzX2RmKSksCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgZndzX2ZpZywgZmlnLmhlaWdodCA9IDE1LCBmaWcud2lkdGggPSAxNSwgZmlnLmFsaWduID0gImNlbnRlciJ9CgpwYXIobWZyb3c9Yyg1LDMpLCBtYXI9Yyg1LCA1LCAyLCAxKSkKb3V0cHV0IDwtIGJ5KGZ3c19kZiwgZndzX2RmJHRheG9ub21pY19ncm91cCwgZnVuY3Rpb24oc3ViKSB7CiAgcGxvdF9tYXQgPC0gd2l0aChzdWIsIHRhcHBseShzcGVjaWVzX2NvdW50LCBsaXN0KGRlY2FkZSwgY3VycmVudF9zdGF0dXMpLCBzdW0pKQogIAogIGJhcnBsb3QocGxvdF9tYXQsIG1haW49c3ViJHRheG9ub21pY19ncm91cFtbMV1dLCBjZXgubWFpbj0xLjUsCiAgICAgICAgICB5bGltID0gYygwLCBtYXgocGxvdF9tYXQsIG5hLnJtPVRSVUUpKzUpLAogICAgICAgICAgY29sPXNlYWJvcm5fcGFsZXR0ZVtzZXFfYWxvbmcocm93Lm5hbWVzKHBsb3RfbWF0KSldLCBiZXNpZGU9VFJVRSkKICBsZWdlbmQoInRvcCIsIHJvdy5uYW1lcyhwbG90X21hdCksIGZpbGw9c2VhYm9ybl9wYWxldHRlW3NlcV9hbG9uZyhyb3cubmFtZXMocGxvdF9tYXQpKV0sIAogICAgICAgICBuY29sPWxlbmd0aChyb3cubmFtZXMocGxvdF9tYXQpKSkKICBib3goKQp9KQpgYGAKCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGEgaHJlZj0iaHR0cHM6Ly93d3cubmFzcy51c2RhLmdvdi9BZ0NlbnN1cy8iIHRhcmdldD0iYmxhbmsiPjxpbWcgc3JjPSJJTUFHRVMvdXNkYS5wbmciIHdpZHRoPSIxMDBweCIvPjwvYT48L2Rpdj4KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlUuUy4gRGVwYXJ0bWVudCBvZiBBZ3JpY3VsdHVyZTogQWdyaWN1bHR1cmUgQ2Vuc3VzPC9zcGFuPgoKYGBge3IgZmlnNCwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDE3LCBmaWcuYWxpZ24gPSAiY2VudGVyIn0Kc3FsIDwtICJTRUxFQ1QgeWVhciwKICAgICAgICAgICAgICAgQ0FTRQogICAgICAgICAgICAgICAgICAgIFdIRU4gZG9tYWluX2NhdGVnb3J5ID0gJycgVEhFTiAnVE9UQUxcbk9QRVJBVElPTlMnCiAgICAgICAgICAgICAgICAgICAgRUxTRSBSRVBMQUNFKFJFUExBQ0UoUkVQTEFDRShkb21haW5fY2F0ZWdvcnksICcoJywgJ1xuKCcpLCAnVE8nLCAnVE9cbicpLCAnT1InLCAnT1JcbicpCiAgICAgICAgICAgICAgIEVORCBBUyBkb21haW5fY2F0ZWdvcnksIAogICAgICAgICAgICAgICB2YWx1ZQogICAgICAgIEZST00gYWdfY2Vuc3VzIAogICAgICAgIFdIRVJFIGRhdGFfaXRlbSA9ICdGQVJNIE9QRVJBVElPTlMgLSBOVU1CRVIgT0YgT1BFUkFUSU9OUyciCgphZ2NlbnN1c19kZiA8LSBkYkdldFF1ZXJ5KGNvbm4sIHNxbCkKCnBsb3RfbWF0IDwtIHdpdGgoYWdjZW5zdXNfZGYsIHRhcHBseSh2YWx1ZSwgbGlzdCh5ZWFyLCBkb21haW5fY2F0ZWdvcnkpLCBzdW0pKQoKcGFyKG1hcj1jKDUsIDUsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIEFncmljdWx0dXJlIENlbnN1czogTnVtYmVyIG9mIEZhcm0gT3BlcmF0aW9ucyIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6NV0sIHlsaW09YygwLCAyRTYrNUU1KSwgYmVzaWRlPVRSVUUsIHhheHQ9Im4iLCB5YXh0PSJuIikKCmF4aXMoc2lkZT0xLCBhdD1jKDMsOSwxNSwyMSwyNywzMywzOSw0NSkrMC41LCBsYWJlbHM9Y29sbmFtZXMocGxvdF9tYXQpLCB0aWNrPUZBTFNFLCBwYWRqPTAuOSwgcG9zPWMoMCwwKSkKYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSwgbGFzPTEpCmxlZ2VuZCgidG9wIiwgcm93Lm5hbWVzKHBsb3RfbWF0KSwgZmlsbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgbmNvbD01KQpgYGAKCmBgYHtyfQpzcWwgPC0gIldJVEggc3ViIEFTICgKICAgICAgICAgIFNFTEVDVCB5ZWFyLAogICAgICAgICAgICAgICAgIGRhdGFfaXRlbSwKICAgICAgICAgICAgICAgICBSRVBMQUNFKAogICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoCiAgICAgICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJFUExBQ0UoZG9tYWluX2NhdGVnb3J5LCAnOycsICcsJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcoJywgJ1xuKCcpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1RPJywgJ1RPXG4nKSwKICAgICAgICAgICAgICAgICAgICAgICAgJyBPUicsICcgT1JcbicpLAogICAgICAgICAgICAgICAgICAgICdMRVNTJywgJ0xFU1NcbicpIEFTIGRvbWFpbl9jYXRlZ29yeSwgCiAgICAgICAgICAgICAgICAgdmFsdWUKICAgICAgICAgIEZST00gYWdfY2Vuc3VzIAogICAgICAgICAgV0hFUkUgZGF0YV9pdGVtIExJS0UgJyVDT01NT0RJVFkgVE9UQUxTJScKICAgICAgICAgICAgQU5EIHZhbHVlIElTIE5PVCBOVUxMCiAgICAgICApIAoKICAgICAgIFNFTEVDVCB5ZWFyLCAKICAgICAgICAgICAgICBDQVNFCiAgICAgICAgICAgICAgICAgIFdIRU4gVFJJTShkb21haW5fY2F0ZWdvcnkpID0gJycgCiAgICAgICAgICAgICAgICAgIFRIRU4gQ0FTRSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdIRU4gZGF0YV9pdGVtID0gJ0NPTU1PRElUWSBUT1RBTFMgLSBTQUxFUzsgTUVBU1VSRUQgSU4gJCcgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUSEVOICdUT1RBTFxuU0FMRVMnCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBXSEVOIGRhdGFfaXRlbSA9ICdDT01NT0RJVFkgVE9UQUxTIC0gU0FMRVM7IE1FQVNVUkVEIElOICQgLyBPUEVSQVRJT04nIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVEhFTiAnVE9UQUxcblNBTEVTXG5QRVIgT1BFUkFUSU9OJwogICAgICAgICAgICAgICAgICAgICAgIEVORAogICAgICAgICAgICAgICAgICBFTFNFIGRvbWFpbl9jYXRlZ29yeSAKICAgICAgICAgICAgICBFTkQgQVMgZG9tYWluX2NhdGVnb3J5LAogICAgICAgICAgICAgIHZhbHVlCiAgICAgICBGUk9NIHN1YgogICAgICAiCgphZ2NlbnN1c19kZiA8LSB3aXRoaW4oZGJHZXRRdWVyeShjb25uLCBzcWwpLAogICAgICAgICAgICAgICAgICAgICAgZG9tYWluX2NhdGVnb3J5IDwtIGZhY3Rvcihkb21haW5fY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkZBUk0gU0FMRVM6IFxuKExFU1NcbiBUSEFOIDIsNTAwICQpIiwgIkZBUk0gU0FMRVM6IFxuKDIsNTAwIFRPXG4gNCw5OTkgJCkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkFSTSBTQUxFUzogXG4oNSwwMDAgVE9cbiA5LDk5OSAkKSIsICJGQVJNIFNBTEVTOiBcbigxMCwwMDAgVE9cbiAyNCw5OTkgJCkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGQVJNIFNBTEVTOiBcbigyNSwwMDAgVE9cbiA0OSw5OTkgJCkiLCAiRkFSTSBTQUxFUzogXG4oNTAsMDAwIFRPXG4gOTksOTk5ICQpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkFSTSBTQUxFUzogXG4oMTAwLDAwMCBUT1xuIDQ5OSw5OTkgJCkiLCAiRkFSTSBTQUxFUzogXG4oNTAwLDAwMCBPUlxuIE1PUkUgJCkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVE9UQUxcblNBTEVTIiwgIlRPVEFMXG5TQUxFU1xuUEVSIE9QRVJBVElPTiIpKQopCgprYWJsZV9zdHlsaW5nKGthYmxlKHRhaWwoYWdjZW5zdXNfZGYpKSwKICAgICAgICAgICAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkKYGBgCgpgYGB7ciBmaWc1LCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTcsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwbG90X21hdCA8LSB3aXRoKGFnY2Vuc3VzX2RmW2FnY2Vuc3VzX2RmJGRvbWFpbl9jYXRlZ29yeSAhPSAnVE9UQUxcblNBTEVTJyxdLCAKICAgICAgICAgICAgICAgICB0YXBwbHkodmFsdWUsIGxpc3QoeWVhciwgZmFjdG9yKGRvbWFpbl9jYXRlZ29yeSkpLCBzdW0pKQoKcGFyKG1hcj1jKDUsIDUsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIEFncmljdWx0dXJlIENlbnN1czogQ29tbW9kaXR5IFRvdGFscyAtIEZhcm0gU2FsZXMiLCBjZXgubWFpbj0xLjUsCiAgICAgICAgY29sPXNlYWJvcm5fcGFsZXR0ZVsxOjVdLCB5bGltPWMoMCwgMUU2KSwgYmVzaWRlPVRSVUUsIHhheHQ9Im4iLCB5YXh0PSJuIikKCmF4aXMoc2lkZT0xLCBhdD1jKDMsOSwxNSwyMSwyNywzMywzOSw0NSw1MSkrMC41LCBsYWJlbHM9Y29sbmFtZXMocGxvdF9tYXQpLCB0aWNrPUZBTFNFLCBwYWRqPTAuOSwgcG9zPWMoMCwwKSkKYXhpcyhzaWRlPTIsIGF0PWF4VGlja3MoMiksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygyKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSwgbGFzPTEpCmxlZ2VuZCgidG9wIiwgcm93Lm5hbWVzKHBsb3RfbWF0KSwgZmlsbD1zZWFib3JuX3BhbGV0dGVbMTo1XSwgbmNvbD01KQpgYGAKCgpgYGB7ciBmaWc2LCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwbG90X21hdCA8LSB3aXRoKGFnY2Vuc3VzX2RmW2FnY2Vuc3VzX2RmJGRvbWFpbl9jYXRlZ29yeSA9PSAnVE9UQUxcblNBTEVTJyxdLCAKICAgICAgICAgICAgICAgICB0YXBwbHkodmFsdWUsIGxpc3QoeWVhciwgZmFjdG9yKGRvbWFpbl9jYXRlZ29yeSkpLCBzdW0pKQoKcGFyKG1hcj1jKDUsIDgsIDIsIDEpKQpiYXJwbG90KHBsb3RfbWF0LCBtYWluPSJVLlMuIEFncmljdWx0dXJlIENlbnN1czogQ29tbW9kaXR5IFRvdGFscyAtIE92ZXJhbGwgRmFybSBTYWxlcyIsIGNleC5tYWluPTEuNSwKICAgICAgICBjb2w9c2VhYm9ybl9wYWxldHRlWzE6NV0sIHlsaW09YygwLCA0RTExKzVFMTApLCBiZXNpZGU9VFJVRSwgeGF4dD0ibiIsIHlheHQ9Im4iLCBzcGFjZT0yKQogICAgICAgIApheGlzKHNpZGU9MSwgYXQ9YygyLjUsNS41LDguNSwxMS41LDE0LjUpLCBsYWJlbHM9cm93Lm5hbWVzKHBsb3RfbWF0KSwgcGFkaj0wLjksIHBvcz1jKDAsMCkpCmF4aXMoc2lkZT0yLCBhdD1heFRpY2tzKDIpLCBsYWJlbHM9Zm9ybWF0KGF4VGlja3MoMiksIGJpZy5tYXJrPScsJywgc2NpZW50aWZpYz1GQUxTRSksIGxhcz0xKQpsZWdlbmQoInRvcCIsIHJvdy5uYW1lcyhwbG90X21hdCksIGZpbGw9c2VhYm9ybl9wYWxldHRlWzE6NV0sIG5jb2w9NSkKYGBgCgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodCI+PGltZyBzcmM9IklNQUdFUy93b3JsZF9tZXRyaWNzLnBuZyIgd2lkdGg9IjMwMHB4Ii8+PC9kaXY+CiMjIyBXb3JsZCBNZXRyaWNzCgpgYGB7cn0Kc3FsIDwtICJXSVRIIHBvcCBBUyAKICAgICAgICAgICAgKFNFTEVDVCBwLnllYXIsCiAgICAgICAgICAgICAgICAgICAgcC5wb3B1bGF0aW9uCiAgICAgICAgICAgICBGUk9NIHdvcmxkX3BvcHVsYXRpb24gcAogICAgICAgICAgICAgV0hFUkUgcC5jb3VudHJ5X25hbWUgPSAnV29ybGQnCiAgICAgICAgICAgICAgIEFORCBwLnllYXIgQkVUV0VFTiAyMDAwIEFORCAyMDE5CiAgICAgICAgICAgICApLAogICAgICAgICAgICAgIAogICAgICAgICAgICAgIGxhbmQgQVMKICAgICAgICAgICAgKFNFTEVDVCBhLnllYXIsCiAgICAgICAgICAgICAgICAgICAgYS5wZXJjZW50X2FyYWJsZQogICAgICAgICAgICAgRlJPTSBhcmFibGVfbGFuZCBhCiAgICAgICAgICAgICBXSEVSRSBhLmNvdW50cnlfbmFtZSA9ICdXb3JsZCcKICAgICAgICAgICAgICAgQU5EIGEueWVhciBCRVRXRUVOIDIwMDAgQU5EIDIwMTkKICAgICAgICAgICAgICksCiAgICAgICAgIAogICAgICAgICAgICAgIGZhdW5hIEFTCiAgICAgICAgICAgIChTRUxFQ1QgaS55ZWFyLAogICAgICAgICAgICAgICAgICAgIFNVTShpLnNwZWNpZXNfY291bnQpIEFTIGFuaW1hbHNfY291bnQKICAgICAgICAgICAgIEZST00gaXVjbl9zcGVjaWVzX2NvdW50IGkKICAgICAgICAgICAgIFdIRVJFIGkueWVhciBCRVRXRUVOIDIwMDAgQU5EIDIwMTkKICAgICAgICAgICAgIEdST1VQIEJZIGkueWVhcgogICAgICAgICAgICAgKSwKICAgICAgICAgCiAgICAgICAgICAgICAgZmxvcmEgQVMKICAgICAgICAgICAgKFNFTEVDVCBwLmFzc2Vzc21lbnRfeWVhciBhcyB5ZWFyLAogICAgICAgICAgICAgICAgICAgIENPVU5UKCopIEFTIHBsYW50c19jb3VudAogICAgICAgICAgICAgRlJPTSBwbGFudHNfYXNzZXNzbWVudHMgcAogICAgICAgICAgICAgV0hFUkUgcC5hc3Nlc3NtZW50X3llYXIgQkVUV0VFTiAyMDAwIEFORCAyMDE5CiAgICAgICAgICAgICAgIEFORCBwLmludGVycHJldGVkX3N0YXR1cyA9ICdUaHJlYXRlbmVkJwogICAgICAgICAgICAgR1JPVVAgQlkgcC5hc3Nlc3NtZW50X3llYXIKICAgICAgICAgICAgICksICAgICAgICAgICAgIAogICAgICAgICAKICAgICAgICAgICAgICBpY2UgQVMKICAgICAgICAgICAgKFNFTEVDVCBzLmRhdGVfeWVhciBhcyB5ZWFyLAogICAgICAgICAgICAgICAgICAgIEFWRyhzLmV4dGVudCkgRklMVEVSKFdIRVJFIHMucmVnaW9uID0gJ0FyY3RpYycpIEFTIGFyY3RpY19zZWFfaWNlX2V4dGVudCwKICAgICAgICAgICAgICAgICAgICBBVkcocy5leHRlbnQpIEZJTFRFUihXSEVSRSBzLnJlZ2lvbiA9ICdBbnRhcmN0aWNhJykgQVMgYW50YXJjdGljX3NlYV9pY2VfZXh0ZW50CiAgICAgICAgICAgICBGUk9NIHNlYV9pY2VfZXh0ZW50IHMKICAgICAgICAgICAgIFdIRVJFIHMuZGF0ZV95ZWFyIEJFVFdFRU4gMjAwMCBBTkQgMjAxOQogICAgICAgICAgICAgR1JPVVAgQlkgcy5kYXRlX3llYXIKICAgICAgICAgICAgICksCiAgICAgICAgICAgICAKICAgICAgICAgICAgICBvY2VhbiBBUwogICAgICAgICAgICAoU0VMRUNUIG8ueWVhciBhcyB5ZWFyLAogICAgICAgICAgICAgICAgICAgIEFWRyhvLnRjbzIpIEFTIHRvdGFsX2NvMiwKICAgICAgICAgICAgICAgICAgICBBVkcoby5waHRzMjVwMCkgQVMgcGhfc2NhbGUKICAgICAgICAgICAgIEZST00gb2NlYW5fZGF0YSBvCiAgICAgICAgICAgICBXSEVSRSBvLnllYXIgQkVUV0VFTiAyMDAwIEFORCAyMDE5CiAgICAgICAgICAgICAgIEFORCBvLnRjbzIgPD4gLTk5OTkgQU5EIG8ucGh0czI1cDAgPD4gLTk5OTkKICAgICAgICAgICAgIEdST1VQIEJZIG8ueWVhcgogICAgICAgICAgICAgKSwKICAgICAgICAgICAgIAogICAgICAgICAgICAgIHRlbXAgQVMKICAgICAgICAgICAgKFNFTEVDVCBnLnllYXIgYXMgeWVhciwKICAgICAgICAgICAgICAgICAgICBBVkcoZy5nbG9iYWxfbWVhbikgQVMgZ2xvYmFsX21lYW4KICAgICAgICAgICAgIEZST00gZ2xvYmFsX3RlbXBlcmF0dXJlIGcKICAgICAgICAgICAgIFdIRVJFIGcueWVhciBCRVRXRUVOIDIwMDAgQU5EIDIwMTkKICAgICAgICAgICAgIEdST1VQIEJZIGcueWVhcgogICAgICAgICAgICAgKQogICAgICAgICAgICAgCiAgICAgICAgIFNFTEVDVCBwb3AueWVhciwgcG9wLnBvcHVsYXRpb24sIGxhbmQucGVyY2VudF9hcmFibGUsIGZhdW5hLmFuaW1hbHNfY291bnQsIGZsb3JhLnBsYW50c19jb3VudCwKICAgICAgICAgICAgICAgIGljZS5hcmN0aWNfc2VhX2ljZV9leHRlbnQsIGljZS5hbnRhcmN0aWNfc2VhX2ljZV9leHRlbnQsIAogICAgICAgICAgICAgICAgb2NlYW4udG90YWxfY28yLCBvY2Vhbi5waF9zY2FsZSwgdGVtcC5nbG9iYWxfbWVhbgogICAgICAgICBGUk9NIHBvcCAKICAgICAgICAgSk9JTiBsYW5kIFVTSU5HICh5ZWFyKQogICAgICAgICBKT0lOIGZhdW5hIFVTSU5HICh5ZWFyKQogICAgICAgICBKT0lOIGZsb3JhIFVTSU5HICh5ZWFyKQogICAgICAgICBKT0lOIGljZSBVU0lORyAoeWVhcikKICAgICAgICAgSk9JTiBvY2VhbiBVU0lORyAoeWVhcikKICAgICAgICAgSk9JTiB0ZW1wIFVTSU5HICh5ZWFyKQogICAgICAgICBPUkRFUiBCWSBwb3AueWVhciIKCmVudl93b3JsZF9kZiA8LSBkYkdldFF1ZXJ5KGNvbm4sIHNxbCkKCmthYmxlX3N0eWxpbmcoa2FibGUodGFpbChlbnZfd29ybGRfZGYpKSwgZm9udF9zaXplID0gMTIsCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3J9CmthYmxlX3N0eWxpbmcoa2FibGUoY29yKGVudl93b3JsZF9kZlstMV0pKSwgZm9udF9zaXplID0gMTIsCiAgICAgICAgICAgICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpCmBgYAoKYGBge3IgZmlnMTAsIGZpZy5oZWlnaHQgPSAxNSwgZmlnLndpZHRoID0gMTUsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQpwYXIobWZyb3c9Yyg0LDIpLCBtYWkgPSBjKDAuMywgMC4yLCAwLjcsIDAuMikpCgpmb3IoeCBpbiBjb2xuYW1lcyhlbnZfd29ybGRfZGYpWzM6bmNvbChlbnZfd29ybGRfZGYpXSkgewogIGxmaXQgPC0gbG9lc3MocGFzdGUoeCwgIn4gcG9wdWxhdGlvbiIpLCBkYXRhPWVudl93b3JsZF9kZikKICAKICBwbG90KGFzLmZvcm11bGEocGFzdGUoeCwgIn4gcG9wdWxhdGlvbiIpKSwgZW52X3dvcmxkX2RmLCAKICAgICAgIG1haW49cGFzdGUoInBvcHVsYXRpb24gYW5kXG4iLCBnc3ViKCJfIiwgIiAiLCB4KSksIGNleC5tYWluPTIsCiAgICAgICB0eXBlPSJwIiwgY29sPXNlYWJvcm5fcGFsZXR0ZVsxXSwgeWF4dD0nbicsIHhheHQ9J24nLCBwY2g9MTkpCiAgYXhpcyhzaWRlPTEsIGF0PWF4VGlja3MoMSksIGxhYmVscz1mb3JtYXQoYXhUaWNrcygxKSwgYmlnLm1hcms9JywnLCBzY2llbnRpZmljPUZBTFNFKSkKICBheGlzKHNpZGU9MiwgYXQ9YXhUaWNrcygyKSwgbGFiZWxzPWZvcm1hdChheFRpY2tzKDIpLCBiaWcubWFyaz0nLCcsIHNjaWVudGlmaWM9RkFMU0UpKQogIHBvcF9vcmRlciA8LSBvcmRlcihlbnZfd29ybGRfZGYkcG9wdWxhdGlvbikKICBsaW5lcyhlbnZfd29ybGRfZGYkcG9wdWxhdGlvbltwb3Bfb3JkZXJdLCBsZml0JGZpdHRlZFtwb3Bfb3JkZXJdLCBjb2w9c2VhYm9ybl9wYWxldHRlWzRdLCBsd2Q9MykKfQoKYGBgCgoKYGBge3J9CmRiRGlzY29ubmVjdChjb25uKQpgYGAKCjxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij48aW1nIHNyYz0iSU1BR0VTL3Jfc2hpbnkucG5nIi8+PC9kaXY+CgojIyMgVXNlciBEYXRhIEFwcGxpY2F0aW9uCgo8ZGl2IHN0eWxlPSJmbG9hdDpjZW50ZXIiPjxpbWcgc3JjPSJJTUFHRVMvZW52X2RhdGEucG5nIiB3aWR0aD0iNDAwcHgiLz48L2Rpdj4KCmBgYHtyfQoKIyBMSU5VWCBTSEVMTCBDT01NQU5EIENBTEwKc3lzdGVtKHBhc3RlMCgiZ25vbWUtdGVybWluYWwgLS0gUnNjcmlwdCAtZSBcImxpYnJhcnkoc2hpbnkpOyBzZXR3ZCgnIiwgZ2V0d2QoKSwgIicpOyBydW5BcHAoJ0Vudmlyb25tZW50REJfU2hpbnlfQXBwLlInKVwiIikpCgpgYGAKCjxociBzdHlsZT0iYm9yZGVyOiBub25lOyBoZWlnaHQ6IDFweDsgYmFja2dyb3VuZC1jb2xvcjogI0NDQzsiLz4KCjxoMj48c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5Db25jbHVzaW9uPC9zcGFuPjwvaDI+Cgo8ZGl2IHN0eWxlPSJmbG9hdDpyaWdodDsiPjxpbWcgc3JjPSJJTUFHRVMvZGF0YV9waXBlbGluZS5wbmciIHdpZHRoPSIzNTBweCIvPjwvZGl2Pgo8YnIvPgoKPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdDsiPjxpbWcgc3JjPSJJTUFHRVMvcG9zdGdyZXNxbF9yLnBuZyIgd2lkdGg9IjEwMHB4Ii8+PC9kaXY+Cjxici8+Cjxici8+CgotIDxoMz48c3BhbiBzdHlsZT0iY29sb3I6ICMzMzY3OTEiLz5Qb3N0Z3JlcyBhcyB0b29sIGluIGRhdGEgc2NpZW5jZSB3b3JrZmxvdzwvaDM+Ci0gPGgzPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPkNsZWFyIGFuZCBleHByZXNzaXZlIFNRTCBwcm9jZXNzaW5nPC9oMz4KLSA8aDM+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+TGV2ZXJhZ2UgcG93ZXJmdWwgZGF0YWJhc2UgZW5naW5lPC9oMz4KLSA8aDM+PHNwYW4gc3R5bGU9ImNvbG9yOiAjMzM2NzkxIi8+RW5oYW5jZWQgYW5kIHJvYnVzdCBwcm9jZXNzIG92ZXIgZmxhdCBmaWxlczwvaDM+Ci0gPGgzPjxzcGFuIHN0eWxlPSJjb2xvcjogIzMzNjc5MSIvPlN0cmVhbWxpbmVkIG9wZW4gc291cmNlIGVjb3N5c3RlbTwvaDM+Cgo8YnIvPgo8YnIvPgoKCg==